(* Ulm's Oberon Library Copyright (C) 1989-1994 by University of Ulm, SAI, D-89069 Ulm, Germany ---------------------------------------------------------------------------- Ulm's Oberon Library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. Ulm's Oberon Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. ---------------------------------------------------------------------------- E-mail contact: oberon@mathematik.uni-ulm.de ---------------------------------------------------------------------------- $Id: NetIO.om,v 1.4 2004/05/21 15:19:03 borchert Exp $ ---------------------------------------------------------------------------- $Log: NetIO.om,v $ Revision 1.4 2004/05/21 15:19:03 borchert performance improvements: - ReadConstStringD prefers Streams.ReadPart and ConstStrings.CreateD, if possible (based on code by Christian Ehrhardt) - WriteConstString uses Streams.Copy instead of a loop that uses Streams.ReadByte and Streams.WriteByte Revision 1.3 1995/03/17 16:28:20 borchert - SizeOf stuff removed - support of const strings added - support of Forwarders added Revision 1.2 1994/07/18 14:18:37 borchert unused variables of WriteString (ch + index) removed Revision 1.1 1994/02/22 20:08:43 borchert Initial revision ---------------------------------------------------------------------------- AFB 6/93 ---------------------------------------------------------------------------- *) MODULE ulmNetIO; (* abstraction for the exchange of Oberon base types which are components of persistent data structures *) IMPORT ConstStrings := ulmConstStrings, Disciplines := ulmDisciplines, Forwarders := ulmForwarders, Streams := ulmStreams, Strings := ulmStrings, SYS := SYSTEM, Types := ulmTypes; TYPE Byte* = Types.Byte; TYPE ReadByteProc* = PROCEDURE (s: Streams.Stream; VAR byte: Byte) : BOOLEAN; ReadCharProc* = PROCEDURE (s: Streams.Stream; VAR char: CHAR) : BOOLEAN; ReadBooleanProc* = PROCEDURE (s: Streams.Stream; VAR boolean: BOOLEAN) : BOOLEAN; ReadShortIntProc* = PROCEDURE (s: Streams.Stream; VAR shortint: SHORTINT) : BOOLEAN; ReadIntegerProc* = PROCEDURE (s: Streams.Stream; VAR integer: INTEGER) : BOOLEAN; ReadLongIntProc* = PROCEDURE (s: Streams.Stream; VAR longint: LONGINT) : BOOLEAN; ReadRealProc* = PROCEDURE (s: Streams.Stream; VAR real: REAL) : BOOLEAN; ReadLongRealProc* = PROCEDURE (s: Streams.Stream; VAR longreal: LONGREAL) : BOOLEAN; ReadSetProc* = PROCEDURE (s: Streams.Stream; VAR set: SET) : BOOLEAN; ReadStringProc* = PROCEDURE (s: Streams.Stream; VAR string: ARRAY OF CHAR) : BOOLEAN; ReadConstStringProc* = PROCEDURE (s: Streams.Stream; domain: ConstStrings.Domain; VAR string: ConstStrings.String) : BOOLEAN; WriteByteProc* = PROCEDURE (s: Streams.Stream; byte: Byte) : BOOLEAN; WriteCharProc* = PROCEDURE (s: Streams.Stream; char: CHAR) : BOOLEAN; WriteBooleanProc* = PROCEDURE (s: Streams.Stream; boolean: BOOLEAN) : BOOLEAN; WriteShortIntProc* = PROCEDURE (s: Streams.Stream; shortint: SHORTINT) : BOOLEAN; WriteIntegerProc* = PROCEDURE (s: Streams.Stream; integer: INTEGER) : BOOLEAN; WriteLongIntProc* = PROCEDURE (s: Streams.Stream; longint: LONGINT) : BOOLEAN; WriteRealProc* = PROCEDURE (s: Streams.Stream; real: REAL) : BOOLEAN; WriteLongRealProc* = PROCEDURE (s: Streams.Stream; longreal: LONGREAL) : BOOLEAN; WriteSetProc* = PROCEDURE (s: Streams.Stream; set: SET) : BOOLEAN; WriteStringProc* = PROCEDURE (s: Streams.Stream; string: ARRAY OF CHAR) : BOOLEAN; WriteConstStringProc* = PROCEDURE (s: Streams.Stream; string: ConstStrings.String) : BOOLEAN; Interface* = POINTER TO InterfaceRec; InterfaceRec* = RECORD readByte*: ReadByteProc; readChar*: ReadCharProc; readBoolean*: ReadBooleanProc; readShortInt*: ReadShortIntProc; readInteger*: ReadIntegerProc; readLongInt*: ReadLongIntProc; readReal*: ReadRealProc; readLongReal*: ReadLongRealProc; readSet*: ReadSetProc; readString*: ReadStringProc; readConstString*: ReadConstStringProc; writeByte*: WriteByteProc; writeChar*: WriteCharProc; writeBoolean*: WriteBooleanProc; writeShortInt*: WriteShortIntProc; writeInteger*: WriteIntegerProc; writeLongInt*: WriteLongIntProc; writeReal*: WriteRealProc; writeLongReal*: WriteLongRealProc; writeSet*: WriteSetProc; writeString*: WriteStringProc; writeConstString*: WriteConstStringProc; END; (* private data structures *) TYPE Discipline = POINTER TO DisciplineRec; DisciplineRec = RECORD (Disciplines.DisciplineRec) if: Interface; END; VAR discID: Disciplines.Identifier; PROCEDURE Swap (VAR a : ARRAY OF SYS.BYTE); VAR i,j : LONGINT; tmp : SYS.BYTE; BEGIN i := 0; j := LEN (a) - 1; WHILE i < j DO tmp := a[i]; a[i] := a[j]; a[j] := tmp; INC (i); DEC (j); END; END Swap; PROCEDURE BitSwap (VAR a : ARRAY OF SYS.BYTE); VAR i,old, bit : LONGINT; new : LONGINT; BEGIN i := 0; WHILE i < LEN (a) DO old := ORD (SYS.VAL (CHAR, a[i])); new := 0; bit := 080H; WHILE old # 0 DO IF ODD (old) THEN INC (new, bit); END; bit := ASH (bit, -1);; old := ASH (old, -1); END; a[i] := SYS.VAL (SYS.BYTE, new); INC (i); END; END BitSwap; PROCEDURE ^ Forward(from, to: Forwarders.Object); PROCEDURE AttachInterface*(s: Streams.Stream; if: Interface); VAR disc: Discipline; BEGIN IF if # NIL THEN NEW(disc); disc.id := discID; disc.if := if; Disciplines.Add(s, disc); ELSE Disciplines.Remove(s, discID); END; Forwarders.Update(s, Forward); END AttachInterface; PROCEDURE GetInterface(s: Streams.Stream; VAR if: Interface); VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN if := disc.if; ELSE if := NIL; END; END GetInterface; PROCEDURE CopyInterface*(from, to: Streams.Stream); VAR if: Interface; BEGIN GetInterface(from, if); AttachInterface(to, if); END CopyInterface; PROCEDURE Forward(from, to: Forwarders.Object); BEGIN (* this check is necessary because of Forwarders.Update *) IF ~(from IS Streams.Stream) OR ~(to IS Streams.Stream) THEN RETURN END; WITH from: Streams.Stream DO WITH to: Streams.Stream DO (* be careful here, from & to must be reversed *) CopyInterface(to, from); END; END; END Forward; PROCEDURE ReadByte*(s: Streams.Stream; VAR byte: Byte) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readByte(s, byte) ELSE RETURN Streams.ReadByte(s, byte) END; END ReadByte; PROCEDURE ReadChar*(s: Streams.Stream; VAR char: CHAR) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readChar(s, char) ELSE RETURN Streams.ReadByte(s, char) END; END ReadChar; PROCEDURE ReadBoolean*(s: Streams.Stream; VAR boolean: BOOLEAN) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readBoolean(s, boolean) ELSE RETURN Streams.Read(s, boolean) END; END ReadBoolean; PROCEDURE ReadShortInt*(s: Streams.Stream; VAR shortint: SHORTINT) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readShortInt(s, shortint) ELSE RETURN Streams.ReadByte(s, shortint) END; END ReadShortInt; PROCEDURE ReadInteger*(s: Streams.Stream; VAR integer: INTEGER) : BOOLEAN; VAR disc: Discipline; ret : BOOLEAN; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readInteger(s, integer) ELSE ret := Streams.Read(s, integer); IF Types.byteorder = Types.littleEndian THEN Swap (integer); END; RETURN ret; END; END ReadInteger; PROCEDURE ReadLongInt*(s: Streams.Stream; VAR longint: LONGINT) : BOOLEAN; VAR disc: Discipline; ret : BOOLEAN; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readLongInt(s, longint) ELSE ret := Streams.Read(s, longint); IF Types.byteorder = Types.littleEndian THEN Swap (longint); END; RETURN ret; END; END ReadLongInt; PROCEDURE ReadReal*(s: Streams.Stream; VAR real: REAL) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readReal(s, real) ELSE RETURN Streams.Read(s, real) END; END ReadReal; PROCEDURE ReadLongReal*(s: Streams.Stream; VAR longreal: LONGREAL) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readLongReal(s, longreal) ELSE RETURN Streams.Read(s, longreal) END; END ReadLongReal; PROCEDURE ReadSet*(s: Streams.Stream; VAR set: SET) : BOOLEAN; VAR disc: Discipline; ret : BOOLEAN; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readSet(s, set) ELSE ret := Streams.Read(s, set); IF Types.byteorder = Types.littleEndian THEN BitSwap (set); END; RETURN ret; END; END ReadSet; PROCEDURE ReadString*(s: Streams.Stream; VAR string: ARRAY OF CHAR) : BOOLEAN; VAR disc: Discipline; ch: CHAR; index: LONGINT; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readString(s, string) ELSE index := 0; WHILE Streams.ReadByte(s, ch) & (ch # 0X) DO IF index + 1 < LEN(string) THEN string[index] := ch; INC(index); END; END; string[index] := 0X; RETURN ~s.error END; END ReadString; PROCEDURE ReadConstStringD*(s: Streams.Stream; domain: ConstStrings.Domain; VAR string: ConstStrings.String) : BOOLEAN; CONST bufsize = 512; VAR length: LONGINT; buf: Streams.Stream; ch: CHAR; disc: Discipline; stringbuf: ARRAY bufsize OF CHAR; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.readConstString(s, domain, string) ELSE IF ReadLongInt(s, length) THEN IF length >= bufsize THEN ConstStrings.Init(buf); IF ~Streams.Copy(s, buf, length) THEN RETURN FALSE END; ConstStrings.CloseD(buf, domain, string); RETURN length = s.count; ELSE IF ~Streams.ReadPart(s, stringbuf, 0, length) THEN RETURN FALSE END; stringbuf[length] := 0X; ConstStrings.CreateD(string, domain, stringbuf); RETURN TRUE END; ELSE RETURN FALSE END; END; END ReadConstStringD; PROCEDURE ReadConstString*(s: Streams.Stream; VAR string: ConstStrings.String) : BOOLEAN; BEGIN RETURN ReadConstStringD(s, ConstStrings.std, string) END ReadConstString; PROCEDURE WriteByte*(s: Streams.Stream; byte: Byte) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeByte(s, byte) ELSE RETURN Streams.WriteByte(s, byte) END; END WriteByte; PROCEDURE WriteChar*(s: Streams.Stream; char: CHAR) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeChar(s, char) ELSE RETURN Streams.WriteByte(s, char) END; END WriteChar; PROCEDURE WriteBoolean*(s: Streams.Stream; boolean: BOOLEAN) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeBoolean(s, boolean) ELSE RETURN Streams.Write(s, boolean) END; END WriteBoolean; PROCEDURE WriteShortInt*(s: Streams.Stream; shortint: SHORTINT) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeShortInt(s, shortint) ELSE RETURN Streams.WriteByte(s, shortint) END; END WriteShortInt; PROCEDURE WriteInteger*(s: Streams.Stream; integer: INTEGER) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeInteger(s, integer) ELSE IF Types.byteorder = Types.littleEndian THEN Swap (integer); END; RETURN Streams.Write(s, integer); END; END WriteInteger; PROCEDURE WriteLongInt*(s: Streams.Stream; longint: LONGINT) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeLongInt(s, longint) ELSE IF Types.byteorder = Types.littleEndian THEN Swap (longint); END; RETURN Streams.Write(s, longint); END; END WriteLongInt; PROCEDURE WriteReal*(s: Streams.Stream; real: REAL) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeReal(s, real) ELSE RETURN Streams.Write(s, real) END; END WriteReal; PROCEDURE WriteLongReal*(s: Streams.Stream; longreal: LONGREAL) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeLongReal(s, longreal) ELSE RETURN Streams.Write(s, longreal) END; END WriteLongReal; PROCEDURE WriteSet*(s: Streams.Stream; set: SET) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeSet(s, set) ELSE IF Types.byteorder = Types.littleEndian THEN BitSwap (set); END; RETURN Streams.Write(s, set) END; END WriteSet; PROCEDURE WriteString*(s: Streams.Stream; string: ARRAY OF CHAR) : BOOLEAN; VAR disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeString(s, string) ELSE RETURN Streams.WritePart(s, string, 0, Strings.Len(string)) & Streams.WriteByte(s, 0X) END; END WriteString; PROCEDURE WriteConstString*(s: Streams.Stream; string: ConstStrings.String) : BOOLEAN; VAR ch: CHAR; buf: Streams.Stream; disc: Discipline; BEGIN IF Disciplines.Seek(s, discID, SYS.VAL(Disciplines.Discipline, disc)) THEN RETURN disc.if.writeConstString(s, string) ELSE IF WriteLongInt(s, string.len) THEN ConstStrings.Open(buf, string); RETURN Streams.Copy(buf, s, string.len) ELSE RETURN FALSE END; END; END WriteConstString; BEGIN discID := Disciplines.Unique(); Forwarders.Register("Streams.Stream", Forward); END ulmNetIO.