MemoryStreaming

Hier könnt Ihr gute, von Euch geschriebene Codes posten. Sie müssen auf jeden Fall funktionieren und sollten möglichst effizient, elegant und beispielhaft oder einfach nur cool sein.
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

MemoryStreaming

Beitrag von cxAlex »

Ich hab hier mal nen kleinen Code geschreiben um große Speicherbereiche in ein einfaches Streamformat umzuwandeln. Die Blockgröße ist variabel, in einem Stream können beliebig viele verschiedene Blockgrößen vorkommen. Der Builder und der Loader sind voneinander unabhängig. Die Navigation erinnert ein bisschen an LinkedLists.

Details:
- Blöcke können nur am Ende eingefügt werden
- Blöcke können nicht mehr gelöscht werden
- Alles besteht nur aus einem Speicherblock

Der Vorteil gegenüber LinkedList besteht darin das ich alles in einem Speicherblock habe und nicht lauter kleine, verkettete Speicherblöcke, wodurch das ganze einfach als Datei usw. gespeichert werden kann. Außerdem ist der Overhead kleiner.

Kleine Beispiel: Du liest eine eine große Datei ein, baust dir daraus mit dem Builder einen Stream, schickst die einzelnen Blöcke über Netzwerk weiter, die Gegenstelle setzt das Ganze mit dem Builder wieder zu einem Stream zusammen und der Loader macht draus wieder eine Datei, ganz einfach :mrgreen: .

Democode am Ende:

Code: Alles auswählen

; ------------------------------------------------------------------------------------
; Memory - Streaming
; Bauen und lesen von MemoryBlöcken in Streamformat mit Variabler Blockgröße
; Overhead pro Block: 2*SizeOf(INTEGER) (x86: 8 Byte, x64: 16 Byte)
; (c) Alexander Aigner
; PB 4.3+ x86/x64 Ascii/Unicode
; ------------------------------------------------------------------------------------

; ------------------------------------------------------------------------------------
; Structures & Prototypes
; ------------------------------------------------------------------------------------

Structure MemoryStream
  *Cur
  *Start
  Size.i
EndStructure

Prototype MS_StartStream(UserData.i)
Prototype MS_StopStream(UserData.i)
Prototype MS_StreamCB(*Mem, MemSize, StreamedSize, UserData.i)

; ------------------------------------------------------------------------------------
; Builder
; ------------------------------------------------------------------------------------

; Builder initialisieren
Procedure MS_Builder_Init()
  Protected *Stream.MemoryStream = AllocateMemory(SizeOf(MemoryStream))
  
  ProcedureReturn *Stream
EndProcedure

; Builder deinitialisieren
Procedure MS_Builder_DeInit(*Stream.MemoryStream)
  FreeMemory(*Stream)
EndProcedure

; Speicherblock zum Stream hinzufügen
Procedure MS_Builder_AddBlock(*Stream.MemoryStream, *Mem, MemSize)
  Protected LastSize
  With *Stream
    If Not \Start
      ; Start - Block erzeugen
      \Start = AllocateMemory(MemSize + 2*SizeOf(INTEGER))
      \Cur = \Start
      \Size = MemSize + 2*SizeOf(INTEGER)
      ; Positionen Schreiben
      PokeI(\Cur, 0)
      PokeI(\Cur + SizeOf(INTEGER), MemSize)
    Else
      LastSize = PeekI(\Cur + SizeOf(INTEGER))
      ; Block anhängen
      \Start = ReAllocateMemory(\Start, \Size + MemSize + 2*SizeOf(INTEGER))
      \Cur = \Start + \Size
      \Size + MemSize + 2*SizeOf(INTEGER)
      ; Positionen Schreiben
      PokeI(\Cur, LastSize)
      PokeI(\Cur + SizeOf(INTEGER), MemSize)
    EndIf
    
    CopyMemory(*Mem, \Cur + 2*SizeOf(INTEGER), MemSize)
  EndWith
EndProcedure

; Stream abschließen
Procedure MS_Builder_Finish(*Stream.MemoryStream)
  Protected LastSize
  With *Stream
    If Not \Start
      \Start = AllocateMemory(4*SizeOf(INTEGER))
      \Cur = \Start
      \Size = 4
    Else
      LastSize = PeekI(\Cur + SizeOf(INTEGER))
      \Start = ReAllocateMemory(\Start, \Size + 2*SizeOf(INTEGER))
      \Cur = \Start + \Size
      \Size + 2*SizeOf(INTEGER)
      ; Positionen Schreiben
      PokeI(\Cur, LastSize)
      PokeI(\Cur + SizeOf(INTEGER), 0)
    EndIf
  EndWith
EndProcedure

; Splittet einen Speicherblock auf
Procedure MS_Builder_Splitt(*Stream.MemoryStream, *Mem, MemSize, BlockSize)
  Protected Count
  Count = Round(MemSize/BlockSize, 0)
  For i = 0 To Count
    If (i + 1)*BlockSize>MemSize
      MS_Builder_AddBlock(*Stream, *Mem + (i*BlockSize), BlockSize-((i + 1)*BlockSize-MemSize))
    Else
      MS_Builder_AddBlock(*Stream, *Mem + (i*BlockSize), BlockSize)
    EndIf
  Next
EndProcedure

; Stream Speicheraddresse
Procedure MS_Builder_GetMem(*Stream.MemoryStream)
  With *Stream
    ProcedureReturn \Start
  EndWith
EndProcedure

; Stream Speichergröße
Procedure MS_Builder_GetMemSize(*Stream.MemoryStream)
  With *Stream
    ProcedureReturn \Size
  EndWith
EndProcedure

; Konvertiert Datei zu Stream
Procedure MS_Builder_FileToStream(*Stream.MemoryStream, FileName.s, BlockSize)
  Protected *Mem, FileSize, File = ReadFile(#PB_Any, FileName)
  If File
    FileSize = Lof(File)
    *Mem = AllocateMemory(FileSize)
    ReadData(File, *Mem, FileSize)
    CloseFile(File)
    MS_Builder_Splitt(*Stream, *Mem, FileSize, BlockSize)
  EndIf
EndProcedure

; Speichert den Stream in einer Datei
Procedure MS_Builder_SaveToFile(*Stream.MemoryStream, FileName.s)
  Protected File = CreateFile(#PB_Any, FileName)
  If File
    WriteData(File, MS_Builder_GetMem(*Stream), MS_Builder_GetMemSize(*Stream))
    CloseFile(File)
  EndIf
EndProcedure

; ------------------------------------------------------------------------------------
; Loader
; ------------------------------------------------------------------------------------

; Loader initialisieren
Procedure MS_Loader_Init(*StreamMemory, MemSize)
  Protected *Stream.MemoryStream = AllocateMemory(SizeOf(MemoryStream))
  With *Stream
    \Start = *StreamMemory
    \Cur = *StreamMemory
    \Size = MemSize
    ProcedureReturn *Stream
  EndWith
EndProcedure

; Loader aus Datei initialisieren
Procedure MS_Loader_InitFromFile(FileName.s)
  Protected *Mem, File = ReadFile(#PB_Any, FileName)
  If File
    FileSize = Lof(File)
    *Mem = AllocateMemory(FileSize)
    ReadData(File, *Mem, FileSize)
    CloseFile(File)
    ProcedureReturn MS_Loader_Init(*Mem, FileSize)
  EndIf
EndProcedure

; Loader deinitialisieren
Procedure MS_Loader_DeInit(*Stream.MemoryStream)
  With *Stream
    FreeMemory(\Start)
    FreeMemory(*Stream)
  EndWith
EndProcedure

; Aktuelle Speicherblockaddresse
Procedure MS_Loader_GetMem(*Stream.MemoryStream)
  With *Stream
    ProcedureReturn \Cur + 2*SizeOf(INTEGER)
  EndWith
EndProcedure

; Aktuelle Speicherblockgröße
Procedure MS_Loader_GetMemSize(*Stream.MemoryStream)
  With *Stream
    ProcedureReturn PeekI(\Cur + SizeOf(INTEGER))
  EndWith
EndProcedure

; zum nächsten Speicherblock
Procedure MS_Loader_NextBlock(*Stream.MemoryStream)
  Protected RtVar
  With *Stream
    \Cur + PeekI(\Cur + SizeOf(INTEGER)) + 2*SizeOf(INTEGER)
    If (\Cur-\Start)> = (\Size-2*SizeOf(INTEGER))
      RtVar = #False
    Else
      RtVar = #True
    EndIf
    ProcedureReturn RtVar
  EndWith
EndProcedure

; zum vorherigen Speicherblock
Procedure MS_Loader_PreviousBlock(*Stream.MemoryStream)
  Protected RtVar, LastSize
  With *Stream
    LastSize = PeekI(\Cur)
    If Not LastSize
      RtVar = #False
    Else
      \Cur-(LastSize + 2*SizeOf(INTEGER))
      RtVar = #True
    EndIf
    ProcedureReturn RtVar
  EndWith
EndProcedure

; zum ersten Speicherblock
Procedure MS_Loader_FirstBlock(*Stream.MemoryStream)
  With *Stream
    \Cur = \Start
  EndWith
EndProcedure

; aktuelle BlockID holen
Procedure MS_Loader_GetBlock(*Stream.MemoryStream)
  With *Stream
    ProcedureReturn \Cur
  EndWith
EndProcedure

; aktuellen Block setzen
Procedure MS_Loader_SetBlock(*Stream.MemoryStream, *Block)
  With *Stream
    \Cur = *Block
  EndWith
EndProcedure

; Stream wieder zusammensetzen
Procedure MS_Loader_Merge(*Stream.MemoryStream)
  Protected BlockSize = 0, Size = 0
  Protected *Mem = AllocateMemory(1)
  Protected Block = MS_Loader_GetBlock(*Stream)
  MS_Loader_FirstBlock(*Stream)
  Repeat
    BlockSize = MS_Loader_GetMemSize(*Stream)
    *Mem = ReAllocateMemory(*Mem, Size + BlockSize)
    CopyMemory(MS_Loader_GetMem(*Stream), *Mem + Size, BlockSize)
    Size + BlockSize
  Until Not MS_Loader_NextBlock(*Stream)
  MS_Loader_SetBlock(*Stream, Block)
  ProcedureReturn *Mem
EndProcedure

; Streamt den Speicher an ein Callback
Procedure MS_Loader_Stream(*Stream.MemoryStream, *StartStream.MS_StartStream, *StopStream.MS_StopStream, *StreamCB.MS_StreamCB, UserData.i)
  Protected CurrentSize, StreamedSize, Block = MS_Loader_GetBlock(*Stream)
  With *Stream
    *StartStream(UserData)
    MS_Loader_FirstBlock(*Stream)
    Repeat
      CurrentSize = MS_Loader_GetMemSize(*Stream)
      StreamedSize + CurrentSize
      *StreamCB(MS_Loader_GetMem(*Stream), CurrentSize, StreamedSize, UserData)
    Until Not MS_Loader_NextBlock(*Stream)
    MS_Loader_SetBlock(*Stream, Block)
    *StopStream(UserData)
  EndWith
EndProcedure

; Konvertiert eine Datei direkt in eine StreamDatei
Procedure MS_Misc_FileToStream(FileName.s, StreamFileName.s, BlockSize)
  Protected *Mem, Builder = MS_Builder_Init()
  MS_Builder_FileToStream(Builder, FileName, BlockSize)
  MS_Builder_Finish(Builder)
  MS_Builder_SaveToFile(Builder, StreamFileName)
  *Mem = MS_Builder_GetMem(Builder)
  MS_Builder_DeInit(Builder)
  FreeMemory(*Mem)
EndProcedure

; Konvertiert eine StreamDatei direkt in eine Datei
Procedure MS_Misc_StreamToFile(StreamFileName.s, FileName.s)
  Protected File, *Mem, Loader = MS_Loader_InitFromFile(StreamFileName)
  *Mem = MS_Loader_Merge(Loader)
  MS_Loader_DeInit(Loader)
  File = CreateFile(#PB_Any, FileName)
  If File
    WriteData(File, *Mem, MemorySize(*Mem))
    CloseFile(File)
    FreeMemory(*Mem)
  EndIf
EndProcedure

Zuletzt geändert von cxAlex am 10.02.2009 15:50, insgesamt 3-mal geändert.
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Beitrag von cxAlex »

Update:


- einfache Splitt & Merge Funktionen

Siehe ersten Post
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Benutzeravatar
cxAlex
Beiträge: 2111
Registriert: 26.06.2008 10:42

Beitrag von cxAlex »

Update:

- MS_Builder_FileToStream(): Schnell eine Datei in einen Stream umwandeln
- MS_Builder_SaveToFile(): StreamingFormat als File speichern
- MS_Loader_InitFromFile(): Eine Datei im StreamingFormat laden
- MS_Loader_Stream(): gesamten Stream an ein Callback streamen

- Beispiel für Dateistreaming am Ende des Code

siehe erster Post
Projekte: IO.pbi, vcpu
Pausierte Projekte: Easy Network Manager, µC Emulator
Aufgegebene Projekte: ECluster

Bild

PB 5.1 x64/x86; OS: Win7 x64/Ubuntu 10.x x86
Antworten