next up previous
Nächste Seite: Zusammenfassung Aufwärts: Persistenz Vorherige Seite: Schnittstelle

Sicherung und Restaurierung größerer Datenstrukturen

Die vorhandene Schnittstelle kann auch bequem im Falle beliebiger dynamischer Datenstrukturen verwendet werden, da die Schnittstellenprozeduren zum Schreiben und Lesen natürlich PersistentObjects.Write und PersistentObjects.Read rekursiv aufrufen dürfen.

Falls Zeiger möglicherweise NIL sind, können auch PersistentObjects.WriteObjectOrNIL und PersistentObjects.ReadObjectOrNIL verwendet werden. So könnte beispielsweise eine Sicherungsprozedur für einen binären Baum aussehen:

Abbildung: Serialisierung eines binären Baumes
\begin{figure}\epsfig{file=perstree.eps}\end{figure}

Abbildung 6.3: Serialisierung einer zyklischen Datenstruktur
\begin{figure}\epsfig{file=persring.eps}\end{figure}

PROCEDURE WriteTree(s: Streams.Stream;
                    tree: PersistentObjects.Object) : BOOLEAN;
BEGIN
   WITH tree: Tree DO
      RETURN PersistentObjects.WriteObject(s, tree.object) &
             PersistentObjects.WriteObjectOrNIL(s, tree.left) &
             PersistentObjects.WriteObjectOrNIL(s, tree.right)
   END;
END WriteTree;

Abbildung 6.2 zeigt das Schema eines entsprechend serialisierten binären Baumes.

Die Rekursion führt natürlich bei zyklischen Datenstrukturen zu Problemen, die mit dem Modul LinearizedStructures elegant vermieden werden können. LinearizedStructures verwaltet über eine private Disziplin bei dem Stream die Liste der bereits gesicherten (bzw. eingelesenen) Objekte und gibt entsprechend bei Wiederholungen Verweise aus und kann sie beim Einlesen wieder interpretieren. Entsprechend ist es beispielsweise möglich, einen zweiseitig verketteten Ring zu sichern (siehe auch Abbildung 6.3):

PROCEDURE WriteRing(s: Streams.Stream;
                    ring: PersistentObjects.Object) : BOOLEAN;
BEGIN
   WITH ring: Ring DO
      RETURN LinearizedStructures.WriteObject(s, ring.object) &
             LinearizedStructures.WriteObject(s, ring.prev) &
             LinearizedStructures.WriteObject(s, ring.next)
END WriteRing;

Dabei sind LinearizedStructures.WriteObject und LinearizedStructures.ReadObject analog zu PersistentObjects.WriteObjectOrNIL und PersistentObjects.ReadObjectOrNIL tolerant gegenüber NIL-Zeigern.

Manchmal ist es aber auch einfacher, eine gesamte dynamische Datenstruktur in einer Prozedur zu sichern (bzw. zu restaurieren), wenn die beteiligten Objekttypen privat definiert werden. Dies spart den Aufwand für mehrere persistente Datentypen. Als Beispiel sei hier die Sicherungsprozedur von LinearLists aus dem Beispiel Collections-166.5gegeben:

CONST
   objectSY = "O"; endSY = "E"; (* encoding for persistent representation *)

(* ... *)

PROCEDURE WriteList(s: Streams.Stream;
                    list: PersistentObjects.Object) : BOOLEAN;
   VAR
      member: Linkable;
BEGIN
   WITH list: List DO
      member := list.head;
      WHILE member # NIL DO
         IF member.object IS PersistentObjects.Object THEN
            IF ~NetIO.WriteChar(s, objectSY) OR
                  ~PersistentObjects.Write(s,
                     member.object(PersistentObjects.Object)) THEN
               RETURN FALSE
            END;
         END;
         member := member.next;
      END;
   END;
   RETURN NetIO.WriteChar(s, endSY)
END WriteList;

Wenn die Anzahl der gesicherten Objekte in so einem Fall variabel ist, empfiehlt es sich, die Zahl der Objekte oder - wie im Beispiel - spezielle Symbole auszugeben, die das korrekte Einlesen nachher ermöglichen. In diesem Beispiel wurde auf diese Weise vermieden, den internen Datentyp Linkable als persistentes Objekt zu definieren. Außerdem ist diese Lösung effizienter, da sie ohne Rekursion auskommt.


next up previous
Nächste Seite: Zusammenfassung Aufwärts: Persistenz Vorherige Seite: Schnittstelle
Andreas Borchert 2000-12-18