Seite 1 von 1

FastPreferences

Verfasst: 12.07.2006 19:17
von remi_meier
Hab das vor zwar schon vor ein paar Monaten fertig gehabt, aber nun ists
mir wieder in den Sinn gekommen, da ichs wieder mal verwenden konnte.

Diese FastPreferences heissen nicht so, weil sie extrem schnell sind,
sondern weil man sie sehr leicht und schnell in einem Programm einbauen
kann.
Gespeichert werden kann alles. Strings, Bilder, Sounds, etc. Wie in einer
Datenbank, nur nicht ganz so schnell (Datensätze werden gesucht, indem
eine Liste von Anfang an durchgegangen wird). Alle Datensätze können
über Strings (Keys wie bei Preferences von PB) angesprochen werden,
also mit Namen :D

Kann benutzt werden für:
- Mehrsprachensupport
- Gameressourcen-Speicherung, oder sonstiges Speichern irgendwelcher
Bilder, Sounds und Daten
- Alles, was Daten speichern muss/soll/darf


Unicode geht auch.
Der Code mit angehängtem Beispiel:

Code: Alles auswählen

; PB 4.0
; Coded by Remi_Meier@gmx.ch in 2006
; If you want to be a thief, just remove this comment.

EnableExplicit


Structure FPREFELEM
  *prev.FPREFELEM
  *next.FPREFELEM
  *name  ; Ptr to String
  *value ; Ptr to Data
EndStructure


Interface iFPref
  addData.l(key.s, *mem, len.l) ; Adds data identified with a key
  addString.l(key.s, string.s) ; Adds a string to the list
  addChar.l(key.s, Char.c) ; Adds a character to the list
  addLong.l(key.s, long.l) ; Adds a long to the list
  addQuad.l(key.s, quad.q) ; Adds a quad to the list
  addFloat.l(key.s, float.f) ; Adds a float to the list
  addDouble.l(key.s, double.d) ; Adds a double to the list
  removeData.l(key.s) ; Removes the data identified with the key
  getDataPtr.l(key.s) ; Gets a pointer to the data
  getString.s(key.s) ; Returns the data as a string, be sure, that Data is a String!
  getChar.c(key.s) ; returns the data as a character
  getLong.l(key.s) ; returns the data as a long
  getQuad.q(key.s) ; returns the data as a quad
  getFloat.f(key.s) ; returns the data as a float
  getDouble.d(key.s) ; returns the data as a double
  clear.l() ; clears the list
  saveToFile.l(file.s) ; Save data list to file
  loadFromFile.l(file.s) ; Loads data from file (adds it to the list!)
  ; Iteration
  examineData() ; Starts the iteration through the data pointers
  nextData.l() ; Selects next data element, returns 0 if none exists
  currentDataPtr.l() ; Returns a pointer to the data in the currently selected element
  currentString.s() ; Returns the string of currentDataPtr(), be sure, that Data is a String!
  currentKey.s() ; Returns the key of the currently selected element
  countKeys.l() ; returns the count of stored keys
EndInterface


Structure FPREFKEY
  hash.l  ; CRC32FingerPrint
  *element.FPREFELEM  ; ptrToElement
EndStructure

Structure cFPref
  *VT
  kCount.l   ; count of keys
  *keys.FPREFKEY  ; Array of FPREFKEYs
  *listHead.FPREFELEM ; Ptr to ListStart
  *currentData.FPREFELEM ; for Iterator
EndStructure

Procedure FPref__freeElement(*this.cFPref, *elem.FPREFELEM)
  If *elem
    If *elem\Name
      FreeMemory(*elem\Name)
    EndIf
    If *elem\Value
      FreeMemory(*elem\Value)
    EndIf
    FreeMemory(*elem)
  EndIf
EndProcedure

Procedure FPref__addKey(*this.cFPref, *name, *element)
  Protected *t.FPREFKEY
  
  If *name And *element
    *this\kCount + 1
    *this\keys = ReAllocateMemory(*this\keys, *this\kCount * SizeOf(FPREFKEY))
    *t         = *this\keys + (*this\kCount - 1) * SizeOf(FPREFKEY)
    *t\hash    = CRC32Fingerprint(*name, MemoryStringLength(*name) * SizeOf(CHARACTER))
    *t\element = *element
  EndIf
EndProcedure

Procedure FPref__removeKey(*this.cFPref, *key.FPREFKEY)
  Protected newSize.l
  
  If *key And *this\keys
    *this\kCount - 1
    newSize = *this\kCount * SizeOf(FPREFKEY)
    MoveMemory(*key + SizeOf(FPREFKEY), *key, *this\keys + newSize - *key)
    *this\keys = ReAllocateMemory(*this\keys, newSize)
  EndIf
EndProcedure

Procedure.l FPref__addElement(*this.cFPref, *elem.FPREFELEM)
  Protected *key.FPREFKEY, found.l, *t.FPREFKEY, z.l, namehash.l
  
  If Not *elem Or Not *elem\Name
    FPref__freeElement(*this, *elem)
    
  Else
    If *this\listHead And *this\keys
      namehash = CRC32Fingerprint(*elem\Name, MemoryStringLength(*elem\Name) * SizeOf(CHARACTER))
      *key = *this\keys
      found = #False
      For z = 1 To *this\kCount
        If *key\hash = namehash And PeekS(*key\element\Name) = PeekS(*elem\Name)
          ; Modify Data
          If *key\element\Value : FreeMemory(*key\element\Value) : EndIf
          *key\element\Value = *elem\Value
          
          found = #True
          Break
        EndIf
        
        *key + SizeOf(FPREFKEY)
      Next
      
      If found
        FreeMemory(*elem\Name)
        FreeMemory(*elem)
        ProcedureReturn #True
      Else
        ; Else add at start
        *elem\Next = *this\listHead
        *elem\Prev = 0
        *this\listHead\Prev = *elem
        *this\listHead = *elem
        ; Add to Keys-List
        FPref__addKey(*this, *elem\Name, *elem)
      EndIf
    Else
      ; Else insert at start
      *elem\Next = 0
      *elem\Prev = 0
      *this\listHead = *elem
      
      FPref__addKey(*this, *elem\Name, *elem)
    EndIf
    
    ProcedureReturn #True
  EndIf
  
  ProcedureReturn #False
EndProcedure



Procedure.l FPref__removeElement(*this.cFPref, *elem.FPREFELEM)
  Protected *key.FPREFKEY, z.l, namehash.l
  
  If Not *elem Or Not *elem\Name Or Not *this\listHead Or Not *this\keys
    FPref__freeElement(*this, *elem)
  Else
    namehash = CRC32Fingerprint(*elem\Name, MemoryStringLength(*elem\Name) * SizeOf(CHARACTER))
    
    *key = *this\keys
    For z = 1 To *this\kCount
      If *key\hash = namehash And PeekS(*key\element\Name) = PeekS(*elem\Name)
        ; Remove it
        If Not *key\element\Prev
          *this\listHead = *key\element\Next
        Else
          *key\element\Prev\Next = *key\element\Next
        EndIf
        
        If *key\element\Next
          *key\element\Next\Prev = *key\element\Prev
        EndIf
        
        FPref__freeElement(*this, *key\element)
        FPref__freeElement(*this, *elem)
        FPref__removeKey(*this, *key)
        
        ProcedureReturn #True
      EndIf
      
      *key + SizeOf(FPREFKEY)
    Next
    
    FPref__freeElement(*this, *elem)
  EndIf
  
  ProcedureReturn #False
EndProcedure

Procedure.l FPref__createElement(*this.cFPref, key.s, *mem, len.l)
  Protected *elem.FPREFELEM
  
  *elem = AllocateMemory(SizeOf(FPREFELEM))
  If Not *elem
    FPref__freeElement(*this, *elem)
    ProcedureReturn #False
  EndIf
  
  If Len(key) > 0
    *elem\Name = AllocateMemory((Len(key) + 1) * SizeOf(CHARACTER))
    If Not *elem\Name
      FPref__freeElement(*this, *elem)
      ProcedureReturn #False
    EndIf
  EndIf
  
  If len > 0 And *mem
    *elem\Value = AllocateMemory(len)
    If Not *elem\Value
      FPref__freeElement(*this, *elem)
      ProcedureReturn #False
    EndIf
  EndIf
  
  
  If Len(key) > 0
    CopyMemory(@key, *elem\Name, (Len(key) + 1) * SizeOf(CHARACTER))
  EndIf
  If len > 0 And *mem
    CopyMemory(*mem, *elem\Value, len)
  EndIf
  
  ProcedureReturn *elem
EndProcedure

;** fpref_addData
;* adds or modifies data of the key.
;** .key: a key to identify the data
;** .*mem: the memory from which to copy
;** .len: len to copy from *mem
Procedure.l FPref_addData(*this.cFPref, key.s, *mem, len.l)
  Protected *elem.FPREFELEM
  
  *elem = FPref__createElement(*this, key, *mem, len)
  
  If FPref__addElement(*this, *elem)
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure

;** fpref_addstring
;* a simplyfied version of addData to add a string
Procedure.l FPref_addString(*this.cFPref, key.s, string.s)
  ProcedureReturn FPref_addData(*this, key, @string, (Len(string) + 1) * SizeOf(CHARACTER))
EndProcedure

;** fpref_removedata
;* removes the element identified by the key
Procedure.l FPref_removeData(*this.cFPref, key.s)
  Protected *elem.FPREFELEM
  
  *elem = FPref__createElement(*this, key, 0, 0)
  
  If FPref__removeElement(*this, *elem)
    ProcedureReturn #True
  Else
    ProcedureReturn #False
  EndIf
EndProcedure

;** fpref_getdataptr
;* returns a pointer to the data of the key
Procedure.l FPref_getDataPtr(*this.cFPref, key.s)
  Protected *key.FPREFKEY, z.l, namehash.l
  
  If key And *this\keys
    namehash = CRC32Fingerprint(@key, Len(key) * SizeOf(CHARACTER))
    *key = *this\keys
    For z = 1 To *this\kCount
      If *key\hash = namehash And PeekS(*key\element\Name) = key
        ProcedureReturn *key\element\Value
      EndIf
      
      *key + SizeOf(FPREFKEY)
    Next
  EndIf
  
  ProcedureReturn #Null
EndProcedure

;** fpref_getstring
;* returns the string identified by the key_
;* simplified version of getdataptr() with PeekS()_
;* <b>Only works if you really saved a string in this key!</b>
Procedure.s FPref_getString(*this.cFPref, key.s)
  Protected *mem
  *mem = FPref_getDataPtr(*this, key)
  If *mem
    ProcedureReturn PeekS(*mem)
  Else
    ProcedureReturn ""
  EndIf
EndProcedure

Procedure.l FPref_getLong(*this.cFPref, key.s)
  Protected *mem
  *mem = FPref_getDataPtr(*this, key)
  If *mem
    ProcedureReturn PeekL(*mem)
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure.l FPref_addLong(*this.cFPref, key.s, long.l)
  ProcedureReturn FPref_addData(*this, key, @long, SizeOf(Long))
EndProcedure

Procedure.c FPref_getChar(*this.cFPref, key.s)
  Protected *mem
  *mem = FPref_getDataPtr(*this, key)
  If *mem
    ProcedureReturn PeekC(*mem)
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure.l FPref_addChar(*this.cFPref, key.s, Char.c)
  ProcedureReturn FPref_addData(*this, key, @Char, SizeOf(CHARACTER))
EndProcedure

Procedure.f FPref_getFloat(*this.cFPref, key.s)
  Protected *mem
  *mem = FPref_getDataPtr(*this, key)
  If *mem
    ProcedureReturn PeekF(*mem)
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure.l FPref_addFloat(*this.cFPref, key.s, float.f)
  ProcedureReturn FPref_addData(*this, key, @float, SizeOf(Float))
EndProcedure

Procedure.q FPref_getQuad(*this.cFPref, key.s)
  Protected *mem
  *mem = FPref_getDataPtr(*this, key)
  If *mem
    ProcedureReturn PeekQ(*mem)
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure.l FPref_addQuad(*this.cFPref, key.s, quad.q)
  ProcedureReturn FPref_addData(*this, key, @quad, SizeOf(Quad))
EndProcedure

Procedure.d FPref_getDouble(*this.cFPref, key.s)
  Protected *mem
  *mem = FPref_getDataPtr(*this, key)
  If *mem
    ProcedureReturn PeekD(*mem)
  Else
    ProcedureReturn 0
  EndIf
EndProcedure

Procedure.l FPref_addDouble(*this.cFPref, key.s, double.d)
  ProcedureReturn FPref_addData(*this, key, @double, SizeOf(Double))
EndProcedure


;** fpref_clear: deletes all keys
Procedure.l FPref_clear(*this.cFPref)
  Protected *list.FPREFELEM, *nextelem.FPREFELEM
  
  If *this\listHead And *this\keys
    *list = *this\listHead
    While *list
      *nextelem = *list\Next
      FPref__freeElement(*this, *list)
      
      *list = *nextelem
    Wend
    
    *this\listHead = 0
    
    FreeMemory(*this\keys)
    *this\kCount = 0
  EndIf
  
  ProcedureReturn #True
EndProcedure

;** fpref_savetofile: saves all keys in the list
;* to a file (binary)
Procedure.l FPref_saveToFile(*this.cFPref, file.s)
  Protected fileID.l, *list.FPREFELEM, len.l
  
  fileID = CreateFile(#PB_Any, file)
  If IsFile(fileID)
    If *this\listHead
      *list = *this\listHead
      While *list
        If *list\Name
          len = MemorySize(*list\Name)
          WriteLong(fileID, len)
          WriteData(fileID, *list\Name, len)
          
          If *list\Name
            len = MemorySize(*list\Value)
          Else
            len = 0
          EndIf
          WriteLong(fileID, len)
          If len
            WriteData(fileID, *list\Value, len)
          EndIf
        EndIf
        
        *list = *list\Next
      Wend
    EndIf
    CloseFile(fileID)
    ProcedureReturn #True
  EndIf
  
  ProcedureReturn #False
EndProcedure

;** fpref_loadfromfile: loads a previously saved list
;* from a file. If the list already contains some 
;* keys, they will be overwritten (if the same Key.s used)
;* and the new keys will be added. No keys will be removed!
Procedure.l FPref_loadFromFile(*this.cFPref, file.s)
  Protected fileID.l, *elem.FPREFELEM, len.l
  
  fileID = ReadFile(#PB_Any, file)
  If IsFile(fileID)
    While Not Eof(fileID)
      *elem = AllocateMemory(SizeOf(FPREFELEM))
      If Not *elem
        ProcedureReturn #False
      EndIf
      
      len = ReadLong(fileID)
      *elem\Name = AllocateMemory(len)
      If *elem\Name
        ReadData(fileID, *elem\Name, len)
      Else
        FreeMemory(*elem)
        ProcedureReturn #False
      EndIf
      
      len = ReadLong(fileID)
      If len
        *elem\Value = AllocateMemory(len)
        If Not *elem\Value
          FreeMemory(*elem\Name)
          FreeMemory(*elem)
          ProcedureReturn #False
        EndIf
        ReadData(fileID, *elem\Value, len)
      EndIf
      
      If Not FPref__addElement(*this, *elem)
        ProcedureReturn #False
      EndIf
    Wend
    
    CloseFile(fileID)
    ProcedureReturn #True
  EndIf
  
  ProcedureReturn #False
EndProcedure

;** fpref_examinedata: Starts examination of all keys in the list
Procedure FPref_examineData(*this.cFPref)
  *this\currentData = 0
EndProcedure

;** fpref_nextdata:
;* Goes to the next key (returns 0 if there is none)
Procedure.l FPref_nextData(*this.cFPref)
  If *this\currentData = 0
    *this\currentData = *this\listHead
  Else
    *this\currentData = *this\currentData\Next
  EndIf
  ProcedureReturn *this\currentData
EndProcedure

;** fpref_currentdataptr
;* returns the dataptr of the current element/key
Procedure.l FPref_currentDataPtr(*this.cFPref)
  If *this\currentData
    ProcedureReturn *this\currentData\Value
  Else
    ProcedureReturn #False
  EndIf
EndProcedure

;** fpref_currentString
;* returns a string of the current element/key.
;* simplified version of currentDataPtr()._
;* <b>Only valid if there is really a string
;* stored in this key!</b>_
;* To be aware of this, you could i. c. add a
;* prefix to all of your keys that identifies
;* of which type the data in this key is. Like:_
;* Key.s = "s CancelButton"
Procedure.s FPref_currentString(*this.cFPref)
  Protected *mem
  *mem = FPref_currentDataPtr(*this)
  If *mem
    ProcedureReturn PeekS(*mem)
  Else
    ProcedureReturn ""
  EndIf
EndProcedure

;** fpref_currentkey
;* returns the key/identifier of the current element/key.
Procedure.s FPref_currentKey(*this.cFPref)
  If *this\currentData
    ProcedureReturn PeekS(*this\currentData\Name)
  Else
    ProcedureReturn ""
  EndIf
EndProcedure

Procedure.l FPref_CountKeys(*this.cFPref)
  ProcedureReturn *this\kCount
EndProcedure

DataSection ;[
  cFPref_VT:
  Data.l @FPref_addData(), @FPref_addString(), @FPref_addChar(), @FPref_addLong()
  Data.l @FPref_addQuad(), @FPref_addFloat(), @FPref_addDouble(), @FPref_removeData()
  Data.l @FPref_getDataPtr(), @FPref_getString(), @FPref_getChar(),@FPref_getLong()
  Data.l @FPref_getQuad(), @FPref_getFloat(), @FPref_getDouble(), @FPref_clear()
  Data.l @FPref_saveToFile(), @FPref_loadFromFile(), @FPref_examineData()
  Data.l @FPref_nextData(), @FPref_currentDataPtr(), @FPref_currentString()
  Data.l @FPref_currentKey(), @FPref_CountKeys()
EndDataSection ;]


Procedure.l new_FPref()
  Protected *this.cFPref
  
  *this = AllocateMemory(SizeOf(cFPref))
  If *this = 0 : ProcedureReturn 0 : EndIf
  *this\vt = ?cFPref_VT
  
  ProcedureReturn *this
EndProcedure

Procedure.l delete_FPref(*this.cFPref)
  FPref_clear(*this)
  FreeMemory(*this)
EndProcedure







; 
; Define.iFPref daten
; daten = new_FPref()
; 
; Define.l z
; Dim toSave.l(2)
; toSave(0) = 1
; toSave(1) = 2
; toSave(2) = 3
; 
; daten\addString("s Hilfe", "Hilfe")
; daten\addString("s Datei", "Datei")
; daten\addString("s Bearbeiten", "Bearbeiten")
; daten\addData("d Array", @toSave(), 3 * 4)
; daten\saveToFile("german.data")
; 
; toSave(0) = 4
; toSave(1) = 5
; toSave(2) = 6
; daten\addString("s Hilfe", "Help")
; daten\addString("s Datei", "File")
; daten\addString("s Bearbeiten", "Edit")
; daten\addData("d Array", @toSave(), 3 * 4)
; daten\saveToFile("english.data")
; 
; 
; Debug "###### German:"
; daten\loadFromFile("german.data")
; daten\examineData()
; While daten\nextData()
  ; If Left(daten\currentKey(), 1) = "s"
    ; Debug daten\currentKey() + ": " + daten\currentString()
  ; Else
    ; CopyMemory(daten\currentDataPtr(), @toSave(), MemorySize(daten\currentDataPtr()))
    ; Debug daten\currentKey() + ":"
    ; For z = 0 To 2
      ; Debug toSave(z)
    ; Next
  ; EndIf
; Wend
; 
; Debug ""
; 
; Debug "###### English:"
; daten\loadFromFile("english.data")
; daten\examineData()
; While daten\nextData()
  ; If Left(daten\currentKey(), 1) = "s"
    ; Debug daten\currentKey() + ": " + daten\currentString()
  ; Else
    ; CopyMemory(daten\currentDataPtr(), @toSave(), MemorySize(daten\currentDataPtr()))
    ; Debug daten\currentKey() + ":"
    ; For z = 0 To 2
      ; Debug toSave(z)
    ; Next
  ; EndIf
; Wend
; 
; delete_FPref(daten)
Ich hoffe es hat immer noch keine Bugs :mrgreen:



Update:
- Speed und Bug fixed :)
- Neue Methoden

Verfasst: 12.07.2006 20:38
von ts-soft
Danke, funktioniert gut!
Wenns nicht stört, werde ich es zum IncludePack hinzufügen?

Gruß
Thomas

PS: hab gerade vor kurzem versucht das DictionaryObject in PB
nachzubilden, aber dieser Code beinhaltet dessen Funktionalität.

Verfasst: 13.07.2006 11:47
von AND51
Die Idee ist ausgesprochen sptize! :allright:
Ich habe es zwar noch nicht getestet, aber die Beschreibung durchgelesen. Wie wäre es, wenn du es dann lieber "EasyPreferences" nennst, um die besagte Verwechslung zu vermeiden?

Verfasst: 13.07.2006 13:56
von remi_meier
@ts-soft:
Kannst du gerne hinzufügen.
> DictionaryObject
Würde mich trotzdem interessieren, was das ist :D

@AND51:
Habs anfangs genau so genannt, aber irgendwie müsste ich dazu alle
'FPref's in 'EPref's umwandeln. Du darfst es nennen wie du willst :mrgreen:

Verfasst: 13.07.2006 17:01
von AND51
remi_meier hat geschrieben:aber irgendwie müsste ich dazu alle
'FPref's in 'EPref's umwandeln.
http://people.freenet.de/tobiasdreissig ... ace-it.zip

Obwohl das ja eigentlich ne Schande ist: Das Tool kann man uach gut selbst nachprogrammieren als PBler, aber trotzdem: Vielleicht ist es ja was für dich. :wink:

Verfasst: 13.07.2006 17:25
von ts-soft
remi_meier hat geschrieben:@ts-soft:
Kannst du gerne hinzufügen.
> DictionaryObject
Würde mich trotzdem interessieren, was das ist :D
Gehört zum WSH, sowas wie ein assoziatives Array

Verfasst: 13.07.2006 20:29
von remi_meier
@AND51: Is dir langweilig? :D

@ts-soft: Ah thx, ein richtiges assoziatives Array müsste glaub auch noch
gemacht werden. Fred hat das auf seiner ToDo Liste (wohl aber ganz zu
unterst) :)

Verfasst: 13.07.2006 20:40
von AND51
remi_meier hat geschrieben:@AND51: Is dir langweilig? :D
Nja... Nein! Mir war ned langweilig :roll: :lol:

Fiel mir nur ein, da ich das Tool selbst benutze, wenn es bei mir Anwendung findet.

Verfasst: 05.12.2006 22:51
von remi_meier
Ich habe diesen Code mal um etwas Hash-SpeedUp ergänzt. Es sollte
nun doch recht schnell sein. Da ich aber relativ viel ändern musste (z. T.
auch nur ein paar Namen, aber auch da können Fehler entstehen),
sollte man den Code ev. noch etwas ausgiebiger testen.

Ich verwende ihn bei mir nun auch für etwas Datenverwaltung, nicht mehr
nur Preferences (deshalb das Update), es sollte nun wohl sicher schnell
genug für die meisten GUI-Anwendungen sein.

Verfasst: 06.12.2006 15:09
von remi_meier
Ich habe noch ein paar weitere Methoden hinzugefügt um Schreibarbeit
zu ersparen.
Kleiner Speed-Test:
10'000 Elemente (jeweils Grösse: 2000 Bytes) hinzufügen und alle genau
einmal finden, dauert hier ~500ms

Die Grösse der Elemente ist nur beim Hinzufügen wichtig, da dort immer
der Speicher kopiert werden muss, das Suchen der Daten ist davon
unabhängig.