Seite 1 von 1

FileMapping für grosse Dateien

Verfasst: 22.05.2007 05:26
von ts-soft
Hier mal ein paar Funktionen um grosse Dateien schnell zu lesen bzw. zu
überschreiben.

Einsatzgebiet sind Hexeditoren, Dateipatcher usw., für Dateien die normal
nicht in den Speicher passen :wink:

Geschwindigkeitszuwachs könnte ihr ja mal selber testen, über doppelt so
schnell wie die nativen PB-Funktionen sollte es immer sein :mrgreen:

Die Funktionsnamen entsprechen denen von PB mit _M für mapped
angehängt.

Für jede Datei ist eine strukturierte Variable vom Typ FileMap anzulegen!

Code: Alles auswählen

Define.FileMap File
oder

Code: Alles auswählen

Global File.FileMap
Allgemeine Funktionen:

Code: Alles auswählen

Result.l = CloseFile_M(*File.FileMap); schließt die gemappte Datei
Result.l = OpenFile_M(*File.FileMap, FileName.s, Append.l = #False); Öffnet Datei zum lesen und schreiben
Result.l = ReadFile_M(*File.FileMap, FileName.s); Öffnet Datei nur zum lesen

Result.l = Eof_M(*File.FileMap)
Result.q = Loc_M(*File.FileMap)
Result.q = Lof_M(*File.FileMap)
Result.l = FileSeek_M(*File.FileMap, pos.q)
Funktionen zum Lesen:
Achtung: wenn der zu lesende Wert über das Dateiende hinausgeht,
ist das Ergebnis #False und der Dateizeiger befindet sich am Dateiende!
Ausnahme ist ReadData_M, hier wird bis zum Dateiende gelesen und die
Anzahl gelesener Bytes als Ergebnis zurückgegeben

Code: Alles auswählen

Result.b = ReadByte_M(*File.FileMap)
Result.c = ReadCharacter_M(*File.FileMap)
Result.q = ReadData_M(*File.FileMap, *Buffer, length.q); Ergebnis sind die tatsächlich gelesenen Bytes
Result.d = ReadDouble_M(*File.FileMap)
Result.f = ReadFloat_M(*File.FileMap)
Result.l = ReadLong_M(*File.FileMap)
Result.q = ReadQuad_M(*File.FileMap)
Result.w = ReadWord_M(*File.FileMap)
Funktionen zum Schreiben:
Im Erfolgsfalle ist das Ergebnis #True
Sollte der zu schreibende Wert über das Dateiende hinausgehen, wird
garnichts geschrieben, es sei den das optionale Flag Append ist #True, der
Dateizeiger befindet sich hinterher immer am Dateiende!

Code: Alles auswählen

Result.l = WriteByte_M(*File.FileMap, value.b)
Result.l = WriteCharacter_M(*File.FileMap, value.c)
Result.l = WriteData_M(*File.FileMap, *Buffer, length.q)
Result.l = WriteDouble_M(*File.FileMap, value.d)
Result.l = WriteFloat_M(*File.FileMap, value.f)
Result.l = WriteLong_M(*File.FileMap, value.l)
Result.l = WriteQuad_M(*File.FileMap, value.q)
Result.l = WriteWord_M(*File.FileMap, value.w)
Und hier der Code:

Code: Alles auswählen

; German forum: http://www.purebasic.fr/german/viewtopic.php?p=152894#152894
; Author: ts-soft
; Date: 22. May 2007
; OS: Windows
; Demo: No

Structure FileMap
  oFile.l
  FileID.l
  hView.l
  pos.q
  maxpos.q
  write.b
  append.b
EndStructure

Procedure CloseFile_M(*File.FileMap)
  If *File\hView <> 0
    If UnmapViewOfFile_(*File\hView)
      CloseHandle_(*File\oFile)
      CloseFile(*File\FileID)
      ProcedureReturn #True
    EndIf
  EndIf
EndProcedure

Procedure OpenFile_M(*File.FileMap, FileName.s, Append.l = #False)
  *File\maxpos = FileSize(FileName)
  If *File\maxpos > 0
    *File\FileID = OpenFile(#PB_Any, FileName)
    If *File\FileID
      *File\oFile = CreateFileMapping_(FileID(*File\FileID), 0, #PAGE_READWRITE, 0, 0, 0)
      If *File\oFile
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_WRITE, 0, 0, 0)
        If *File\hView
          *File\pos = 0
          *File\write = #True
          If Append = #True
            *File\append = #True
          EndIf
          ProcedureReturn #True
        EndIf
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure CreateFile_M(*File.FileMap, FileName.s, Size.q, Append.l = #False)
  Protected FileID.l = CreateFile(#PB_Any, FileName)
  If FileID
    FileSeek(FileID, Size)
    SetEndOfFile_(FileID(FileID))
    CloseFile(FileID)
    ProcedureReturn OpenFile_M(*File, FileName, Append)
  EndIf
EndProcedure

Procedure ReadFile_M(*File.FileMap, FileName.s)
  *File\maxpos = FileSize(FileName)
  If *File\maxpos > 0
    *File\FileID = ReadFile(#PB_Any, FileName)
    If *File\FileID
      *File\oFile = CreateFileMapping_(FileID(*File\FileID), 0, #PAGE_READONLY, 0, 0, 0)
      If *File\oFile
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          *File\pos = 0
          *File\write = #False
          ProcedureReturn #True
        EndIf
      EndIf
    EndIf
  EndIf
EndProcedure

Procedure Eof_M(*File.FileMap)
  If *File\maxpos <= *File\pos
    ProcedureReturn #True
  EndIf
EndProcedure

Procedure.q Loc_M(*File.FileMap)
  ProcedureReturn *File\pos
EndProcedure

Procedure.q Lof_M(*File.FileMap)
  ProcedureReturn *File\maxpos
EndProcedure

Procedure FileSeek_M(*File.FileMap, pos.q)
  If pos <= *File\maxpos And pos >= 0
    *File\pos = pos
    ProcedureReturn #True
  EndIf
EndProcedure

Procedure.b ReadByte_M(*File.FileMap)
  Protected *result.Byte = *File\hView + *File\pos
  If *File\maxpos >= *File\pos + SizeOf(Byte)
    *File\pos + SizeOf(Byte)
    ProcedureReturn *result\b
  Else
    *File\pos = *File\maxpos
  EndIf
EndProcedure

Procedure.c ReadCharacter_M(*File.FileMap)
  Protected *result.Character = *File\hView + *File\pos
  If *File\maxpos >= *File\pos + SizeOf(Character)
    *File\pos + SizeOf(Character)
    ProcedureReturn *result\c
  Else
    *File\pos = *File\maxpos
  EndIf
EndProcedure

Procedure.q ReadData_M(*File.FileMap, *Buffer, length.q)
  Protected *result = *File\hView + *File\pos
  If *File\pos + length <= *File\maxpos
    CopyMemory(*result, *Buffer, length)
    *File\pos + length
    ProcedureReturn length
  Else
    length = *File\maxpos - *File\pos
    CopyMemory(*result, *Buffer, length)
    *File\pos = *File\maxpos
    ProcedureReturn length
  EndIf
EndProcedure

Procedure.d ReadDouble_M(*File.FileMap)
  Protected *result.Double = *File\hView + *File\pos
  If *File\maxpos >= *File\pos + SizeOf(Double)
    *File\pos + SizeOf(Double)
    ProcedureReturn *result\d
  Else
    *File\pos = *File\maxpos
  EndIf
EndProcedure

Procedure.f ReadFloat_M(*File.FileMap)
  Protected *result.Float = *File\hView + *File\pos
  If *File\maxpos >= *File\pos + SizeOf(Float)
    *File\pos + SizeOf(Float)
    ProcedureReturn *result\f
  Else
    *File\pos = *File\maxpos
  EndIf
EndProcedure

Procedure.l ReadLong_M(*File.FileMap)
  Protected *result.Long = *File\hView + *File\pos
  If *File\maxpos >= *File\pos + SizeOf(Long)
    *File\pos + SizeOf(Long)
    ProcedureReturn *result\l
  Else
    *File\pos = *File\maxpos
  EndIf
EndProcedure

Procedure.q ReadQuad_M(*File.FileMap)
  Protected *result.Quad = *File\hView + *File\pos
  If *File\maxpos >= *File\pos + SizeOf(Quad)
    *File\pos + SizeOf(Quad)
    ProcedureReturn *result\q
  Else
    *File\pos = *File\maxpos
  EndIf
EndProcedure

Procedure.s ReadString_M(*File.FileMap)
  Protected *result.Byte = *File\hView + *File\pos
  Protected tmp.l = *result
  Protected length.l, finish.l
  Repeat
    Select *result\b
      Case 0
        finish = #True
        *File\pos + length
      Case 13, 10
        finish = #True
        *File\pos + length + 1
        *result + 1
        If *result\b = 13 Or *result\b = 10
          *File\pos + 1
        EndIf
      Default
        length + 1
        *result + 1
    EndSelect
  Until finish
  ProcedureReturn PeekS(tmp, length, #PB_Ascii)
EndProcedure

Procedure.w ReadWord_M(*File.FileMap)
  Protected *result.Word = *File\hView + *File\pos
  If *File\maxpos >= *File\pos + SizeOf(Word)
    *File\pos + SizeOf(Word)
    ProcedureReturn *result\w
  Else
    *File\pos = *File\maxpos
  EndIf
EndProcedure

Procedure WriteByte_M(*File.FileMap, value.b)
  If *File\write
    Protected *result.Byte = *File\hView + *File\pos
    If *File\maxpos >= *File\pos + SizeOf(Byte)
      *result\b = value
      *File\pos + SizeOf(Byte)
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteByte(*File\FileID, value)
        *File\pos + SizeOf(Byte)
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure

Procedure WriteCharacter_M(*File.FileMap, value.c)
  If *File\write
    Protected *result.Character = *File\hView + *File\pos
    If *File\maxpos >= *File\pos + SizeOf(Character)
      *result\c = value
      *File\pos + SizeOf(Character)
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteCharacter(*File\FileID, value)
        *File\pos + SizeOf(Character)
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure

Procedure WriteData_M(*File.FileMap, *Buffer, length.q)
  If *File\write
    Protected *result = *File\hView + *File\pos
    If *File\pos + length <= *File\maxpos
      CopyMemory(*Buffer, *result, length)
      *File\pos + length
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteData(*File\FileID, *Buffer, length)
        *File\pos + length
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure

Procedure WriteDouble_M(*File.FileMap, value.d)
  If *File\write
    Protected *result.Double = *File\hView + *File\pos
    If *File\maxpos >= *File\pos + SizeOf(Double)
      *result\d = value
      *File\pos + SizeOf(Double)
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteDouble(*File\FileID, value)
        *File\pos + SizeOf(Double)
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure

Procedure WriteFloat_M(*File.FileMap, value.f)
  If *File\write
    Protected *result.Float = *File\hView + *File\pos
    If *File\maxpos >= *File\pos + SizeOf(Float)
      *result\f = value
      *File\pos + SizeOf(Float)
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteFloat(*File\FileID, value)
        *File\pos + SizeOf(Float)
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure

Procedure WriteLong_M(*File.FileMap, value.l)
  If *File\write
    Protected *result.Long = *File\hView + *File\pos
    If *File\maxpos >= *File\pos + SizeOf(Long)
      *result\l = value
      *File\pos + SizeOf(Long)
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteLong(*File\FileID, value)
        *File\pos + SizeOf(Long)
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure

Procedure WriteQuad_M(*File.FileMap, value.q)
  If *File\write
    Protected *result.Quad = *File\hView + *File\pos
    If *File\maxpos >= *File\pos + SizeOf(Quad)
      *result\q = value
      *File\pos + SizeOf(quad)
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteQuad(*File\FileID, value)
        *File\pos + SizeOf(Quad)
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure

Procedure WriteString_M(*File.FileMap, value.s)
  Protected value_a.s
  CompilerIf #PB_Compiler_Unicode
    value_a = Space(StringByteLength(value, #PB_Ascii))
    PokeS(@value_a, value, #PB_Any, #PB_Ascii)
  CompilerElse
    value_a = value 
  CompilerEndIf
  ProcedureReturn WriteData_M(*File, @value_a, Len(value))
EndProcedure

Procedure WriteStringN_M(*File.FileMap, value.s)
  value + #CRLF$
  Protected value_a.s
  CompilerIf #PB_Compiler_Unicode
    value_a = Space(StringByteLength(value, #PB_Ascii))
    PokeS(@value_a, value, #PB_Any, #PB_Ascii)
  CompilerElse
    value_a = value 
  CompilerEndIf
  ProcedureReturn WriteData_M(*File, @value_a, Len(value))
EndProcedure

Procedure WriteWord_M(*File.FileMap, value.w)
  If *File\write
    Protected *result.Word = *File\hView + *File\pos
    If *File\maxpos >= *File\pos + SizeOf(Word)
      *result\w = value
      *File\pos + SizeOf(Word)
      ProcedureReturn #True
    ElseIf *File\append
      If UnmapViewOfFile_(*File\hView)
        FileSeek(*File\FileID, *File\pos)
        WriteWord(*File\FileID, value)
        *File\pos + SizeOf(Word)
        *File\maxpos = *File\pos
        *File\hView = MapViewOfFile_(*File\oFile, #FILE_MAP_READ, 0, 0, 0)
        If *File\hView
          ProcedureReturn #True
        EndIf
      EndIf
    Else
      *File\pos = *File\maxpos
    EndIf
  EndIf
EndProcedure
Nutzung auf eigene Gefahr, Verbesserungsvorschläge, Fehlermeldungen
usw. willkommen.

Verfasst: 22.05.2007 08:19
von Rings
fastfile schön sauber neu programmiert !!!
(alle mal besser als ich in purem ASM ) ;)

Wenns übrigens richtig schnell gehen soll, so kann man noch auf das
copyMemory teilweise verzichten und direkt schreiben
auf den MapPointer, der ja auch nur ne speicheradresse darstellt.

Und, man könnte auch das drüberschreiben(größere Dateien)
und neu erstellen machen, iss halt nur ein wenig bissl mehr arbeit.
einfach unmappen, reinschreiben und neu mappen.
Dann wärs zumind. noch mehr kompatibel zu bisherigen Lib.

Verfasst: 22.05.2007 08:45
von ts-soft
Rings hat geschrieben:fastfile schön sauber neu programmiert !!!
(alle mal besser als ich in purem ASM ) ;)
Mit dem ASM-Code konnte ich ja nichts anfangen :mrgreen: , also hab ichs
ganz Neu gemacht, zumal Dein Stil schwierig zu lesen ist.
Rings hat geschrieben: Wenns übrigens richtig schnell gehen soll, so kann man noch auf das
copyMemory teilweise verzichten und direkt schreiben
auf den MapPointer, der ja auch nur ne speicheradresse darstellt.
Meinste jetzt ReadData_M und WriteData_M? Weil der Rest wird ja direkt
reingeschrieben, da sollte nichts schneller gehen. Vielleicht kannste das noch
etwas genauer beschreiben?
Rings hat geschrieben: Und, man könnte auch das drüberschreiben(größere Dateien)
und neu erstellen machen, iss halt nur ein wenig bissl mehr arbeit.
einfach unmappen, reinschreiben und neu mappen.
Dann wärs zumind. noch mehr kompatibel zu bisherigen Lib.

Das hatte ich mir auch schon so überlegt, ich wollte es aber erstmal so
ausgetested haben. Bei Stringsfunktionen lag ich beim Speed hinter PB,
habs also erstmal weggelassen, bis es mir kommt :mrgreen:

Dann hoffe ich mal, das bis jetzt noch keine Fehler bei sind,
Fehlerbehandlung muß auch noch DAU-freundlicher werden

Danke Dir,
Gruß
Thomas

Verfasst: 22.05.2007 09:26
von Rings
ts-soft hat geschrieben: Mit dem ASM-Code konnte ich ja nichts anfangen :mrgreen: , also hab ichs
ganz Neu gemacht, zumal Dein Stil schwierig zu lesen ist.
Ich würds heut auch nimmer in ASM machen, die paar cyclen
bringens nichts.
ts-soft hat geschrieben:Meinste jetzt ReadData_M und WriteData_M? Weil der Rest wird ja direkt
reingeschrieben, da sollte nichts schneller gehen. Vielleicht kannste das noch
etwas genauer beschreiben?
Jo, genauso: Ob wohl ich auch hier denke das ein neu schreiben der CopyMemory nich unbedingt besonders was an speed bringt.
ts-soft hat geschrieben:Bei Stringsfunktionen lag ich beim Speed hinter PB,
habs also erstmal weggelassen, bis es mir kommt :mrgreen:
Uh, wusst gar net das StringFunktionen so erogen sind.
Auf alle Fälle gibts da sicherlich Potential ums allemal schneller zu machen
als in PB. Zur Not halt ein wenig InlineASM.
Man muss ja nur nach CHR(0) suchen.......
ts-soft hat geschrieben: Dann hoffe ich mal, das bis jetzt noch keine Fehler bei sind,
Fehlerbehandlung muß auch noch DAU-freundlicher werden
DAU's haben eine seltene Gabe.......

Verfasst: 22.05.2007 10:21
von dige
:allright: Saubere Arbeit! Danke TS

Verfasst: 22.05.2007 10:26
von ts-soft
Rings hat geschrieben:Man muss ja nur nach CHR(0) suchen.......
Leider nicht, sondern nach #LF$, #CR$ und 0, wobei die #LF$ und CR$ ja
auch noch ausgefiltert werden müssen. Muß also Charweise durch den
Speicher rutschen, Filtern, aber den Zeiger um die ungefilterten Chars ver-
schieben, bisher dauert es zu lange :( :mrgreen:

Als Extra-Include nur für reine Textdateien wäre es einfacher, aber bei
gemischten Dateien ist PB schneller.

Verfasst: 22.05.2007 12:19
von ts-soft
Update, siehe erstes Posting!

Jetzt kann auch über das Dateiende hinaus geschrieben werden, wenn bei
OpenFile_M() der optionale Parameter Append auf #True gesetzt wird.
Geschwindigkeitszuwachs gibts beim anhängen nicht, wer also in der Haupt-
sache Daten anhängen möchte, sollte lieber die nativen PB-Funktionen
nehmen!

Achtung: Append setzt den Dateizeiger nicht autom. ans Ende!


// Edit
Hier noch ein kleiner Speedtest, der den wahren Geschwindigkeitsvorteil aber
garnicht aufzeigt, da die PB-Hilfe ja nur knapp 2 MB gross ist.

Code: Alles auswählen

; Pfad und Namen bitte anpassen
XIncludeFile #PB_Compiler_Home + "Includes\FileMapping_Include.pbi"

Define.FileMap File
Define.l time1, time2, time3

time1 = ElapsedMilliseconds()
If ReadFile_M(File, #PB_Compiler_Home + "PureBasic.chm")
  While Not Eof_M(File)
    ReadByte_M(File)
  Wend
  CloseFile_M(File)
EndIf

time2 = ElapsedMilliseconds()
If ReadFile(0, #PB_Compiler_Home + "PureBasic.chm")
  While Not Eof(0)
    ReadByte(0)
  Wend
  CloseFile(0)
EndIf

time3 = ElapsedMilliseconds()

Define.s Map, PB
Map = "Map: " + Str(time2 - time1) + #LF$
PB  = "PB:  " + Str(time3 - time2)

MessageRequester("Zeit", Map + PB, #MB_ICONINFORMATION)

Verfasst: 23.05.2007 05:40
von ts-soft
Update:

CreateFile_M hinzugefügt:

Code: Alles auswählen

Result.l = CreateFile_M(*File.FileMap, FileName.s, Size.q, Append.l = #False)
Erstelle eine neue leere Datei in der angegebenen Grösse, so das man diese
füllen kann. Um Geschwindigkeitsvorteile zu erzielen, sollte man unbedingt
eine Anfangsgrösse angeben, weil nur diese wird auch gemapped.

Stringfunktionen hinzugefügt, aber nur ASCII wird gelesen, bzw. geschrieben, unabhängig vom Programm-Modus

Code: Alles auswählen

Result.s = ReadString_M(*File.FileMap)

Result.l = WriteString_M(*File.FileMap, value.s)
Result.l = WriteStringN_M(*File.FileMap, value.s)