Die definierten Makros und Funktionen für diese Strings erlauben vielfältige, flexible und hochgradig schnelle Operationen. Ein komfortabler, objektorientierter Umgang ist möglich.
Zum aktuellen Zeitpunkt sind noch nicht alle Funktionen realisiert.
Es werden weitere Funktionen fortlaufend hinzugefügt werden.
Dieses Dokument wird ebenso fortlaufend aktualisiert.
Es ist bisher etwa eine Woche Entwicklungszeit im ersten Schub entstanden.
Die C-Quelle ist stets eine
vollständige Informationsquelle für alle Details.
Eine Verwendung von malloc() ist zumindest
bis jetzt vermieden worden, da dies generell die Performance schädigt.
Außerdem sind heutzutage Motherboards (Xeon) erhältlich, die mit bis
zu 7 Terabyte RAM bestückt werden können.
Normale PCs stehen heute meist bei 32 Gigabyte.
Ein Stack-Limit (ulimit) lautet: 524 MB.
Es kann folglich beim Anlegen von Datenobjekten relativ großzügig vorgegangen werden.
Der Typ size_t ist bei einer 64bit-Kompilierung 64 Bit breit und bei einer 32bit-Kompilierung 32 Bit breit. Dies unter Unix sowie auch unter Windows. Zu diesen Bit-Breiten sind jeweils auch Prozessor-Register vorhanden. Aus diesen Gründen wurde dieser Typ bei den grundlegenden Definitionen CTLTYP und CTLWID eingesetzt.
Der Quellcode dieser String-Klasse ist portabler interner Library-Code und muß auch als solcher betrachtet werden. Anwendungscode ist nur in den diversen Testfunktionen am Ende der Quelle vorhanden. Wichtig für den Anwender sind vor allem die Funktionsbeschreibungen unten in dieser Dokumentation.
Die Funktionen dieser String-Klasse sind absolut sicher.
Ein Puffer-Überlauf ist konzeptionell ausgeschlossen.
Bevor kopiert wird, wird berechnet, ob ein Überlauf beim Kopieren passieren würde.
Falls ja, wird der Prozeß beendet.
Eine teilweise Aktion, wie sie bei strncpy()
möglich ist, ist nicht akzeptabel, da nachfolgende Operationen
eine vollständige vorhergehende Aktion voraussetzen (müssen).
Diese Vorberechenbarkeit und die realisierten Vorberechnungen sind ein
systematisches Konzept dieser String-Klasse.
Dies gibt Sicherheit und hohe Arbeitsgeschwindigkeit, weil in Schleifen
keine Prüfungen bei jedem Schritt notwendig sind, ob irgendeine
Grenzenberührung stattfindet.
Die Funktion stringmov() ist ein Parade-Beispiel dafür.
Es werden zwei auseinander positionierte MAGIC-Nummern auf Vorhandensein in jedem String-Objekt geprüft, in jeder relevanten Funktion. Zuvor wird auf Null-Pointer geprüft. Null-Pointer und nicht passende MAGIC-Nummern werden nicht akzeptiert. Ein String-Objekt muß sich gültig identifizieren.
Da das Konzept keinen Terminierungs-Wert vorsieht, dürfen diese Strings inhaltlich sämtliche Werte beliebig enthalten. Eine nachträgliche Null-Terminierung kann mittels der Funktion string0t() vorgenommen werden.
Neben der Länge len sind zusätzlich die Parameter Offset off und Breite wid vorhanden. Für alle drei Parameter sind dedizierte Funktionen zum Setzen des jeweiligen Wertes vorhanden. Die Länge gilt ab Offset. Deren Änderung ändert keine anderen Parameter. Erhöhung des Offsets verringert die Länge, und umgekehrt. Eine Änderung der Breite ändert Länge und Offset. Die Funktionen zum Setzen sind am String-Element (mit seiner Breite) orientiert, nicht am Byte.
Ein String-Objekt kann schreibgeschützt werden:
Anwenderfunktionen berücksichtigen dieses read_only.
Der Schreibschutz gilt für den Inhalts-Puffer, nicht
für den Kontrollbereich (CNTL).
Das String-Objekt, wie auch sein Puffer darin, sind
AL-aligned (z.B. 16).
Daher kann beispielsweise die Funktion stringcpy()
stets die gesamte Kopiermenge in Stücken von AL
Byte kopieren.
Die Funktion stringcats() kopiert durch einen
ultimativen Algorithmus ebenfalls in maximal großen Stücken.
Diese String-Klasse wurde getestet, indem diese Strings mit den normalen
null-terminierten C-Strings verglichen wurden.
Die Testfunktionen befinden sich in der C-Quelle.
Plattform: X86_64, LP, CoreDuo-Prozessor 3333 MHz aus 2007, DDR2.
Im Stack wurde jeweils 77 MB Speicherplatz alloziert
und durch fortschreitendes Kopieren mit Daten gefüllt.
Eine Füll-Geschwindigkeit von 2,9 GB/s 1-fach wurde erreicht.
Resultat: Kopieren mit dieser String-Klasse ist 1,44-mal schneller
als mit den normalen C-Strings.
Das ist ein relativ geringer Vorsprung, was daran liegt, daß die C-Strings
seit Ende der 1980er Jahre kontinuierlich immer weiter verbessert wurden.
Es werden bis zu 8 Byte breite Objekte im Zusammenhang
mit allerlei Tricks verwendet.
Ein Einfluß des Wertes von AL
auf die Kopier-Geschwindigkeit konnte
auf dieser Plattform nicht festgestellt werden.
Es scheint da eine Deckelung zu wirken;
Schneller geht es offenbar nicht.
Angelegt wird ein
Die Objektgröße sizeof(array)-MAXWID in
Byte wird beim Initialisieren in der ersten
Kontroll-Komponente size gespeichert.
Für den Puffer bleiben die Bytes
Variabel sind Anzahl Elemente und Elementbreite (len, wid):
bufsize = len*wid; len = bufsize/wid; wid = bufsize/len;
Das angelegte
8 + 8 + 8 + 8 Byte | 4 + 8 + 4 + 4 Byte
Der Puffer kann 8 … # Byte groß sein.
Eines der 8 Bytes (cntl) besteht
aus einem Bitfeld mit 8 Steuerbits (CNTLB).
Das Makro CNTLP ist für den bequemen
und konsistenten Zugriff auf die Steuerbits vorhanden.
Gleichzeitiger Zugriff per Bitfeld (struct)
und mittels Bit-Operationen passen nicht mehr zusammen nach Änderung der Endianness!
Der Typ XT ist ein
union-Typ mit mehreren verschiedenen Typen darin.
Die roten Punkte • stehen
für
Die Kontrollkomponenten size, off und len müssen
(hier) nicht (mehr) näher erklärt werden.
Puffer buf.xx[] ebenso nicht.
Die Kontroll-Bytes mag1 und mag2 enthalten die beiden
MAGIC-Zahlen. Erklärung unten bei den Funktionen.
Das Byte wid enthält die Breite der Array-Elemente
in Byte: sizeof(array[0]).
Dies ist eine Potenz von 2. Dazu gehört shft, um Division und Multiplikation
per Schiebe-Instruktion zu ermöglichen.
Byte swid kann die Breiten von Strings {L|u|U|u8}"..."
und Multibyte-Zeichen (1) enthalten.
Byte cntl ist ein Bitfeld und oben und nachfolgend eingehend erklärt.
Das Füllzeichen filc wird benutzt, um bei Operationen mit Länge
und Offset entstehenden Leerraum zu füllen.
Erklärung unten bei den Funktionen.
Die Steuerbits lfil und ofil entscheiden über das Füllen
des im Byte filc enthaltenen Füllzeichens, im Zusammenhang
mit Längen- und Offsetoperationen.
Die Bedeutung der Bits sign und text dürfte klar sein.
Aktuell ist text ohne Verwendung.
Der Bit-Name ro steht für read_only und ist ein Schreibschutz
für den Inhalts-Puffer eines String-Objekts. Erklärung auch oben.
Die Bits text und ofil werden initial auf 1 gesetzt.
Es kann ein Strukturtyp STRING mit flexibler
Array-Komponente[1] mit dem angelegten
Die Formulierung STRING *sp= (STRING*)arrayname; verknüpft
den Typ STRING* mit einem uchar array[ne].
Die effektive Adresse ist die vom Array, das der Struktur STRING
untergeschoben wurde.
Das ist genau so, wie hier:
d_name[1] ,
als es das flexible array[] des C-Standards C99 noch nicht gab.
Ein flexibles Array des C99 kann in einer union nicht
verwendet werden, weshalb es sein könnte, daß der Compiler Fehler gibt, falls
eine sichtbare Konstante als Array[index] verwendet wird.
Eine Alternative wären mehrere Strukturen mit jeweils flexibler
Array[]-Komponente darin, innerhalb einer union.
Die Ansprache im Code wäre dann jedoch sehr unhandlich.
Die Funktion main() zeigt Test-Operationen.
Es sind die beiden Methoden des Anlegens von String-Objekten gezeigt:
Außerhalb und innerhalb von Funktionen, jeweils ohne und mit dem
Speicherklassen-Spezifizierer static.
Das Makro STRING_DuI() kann nur innerhalb
einer Funktion angewandt werden;
Es nimmt Definition und Initialisierung gleichzeitig vor.
Ein Anlegen mittels des Makros STRING_DEF() erfordert
anschließend die Benutzung des Initialisierungs-Makros
STRING_INI() innerhalb einer Funktion.
String-Objekte können beliebig oft initialisiert werden.
Die Größenangabe bei den Makros() ist eine Anzahl Bytes, wie auch
bei der Funktion malloc().
Dementsprechend kann eine Angabe beispielsweise (name,4*1000)
oder (name,sizeof(int)*1000) lauten.
a:BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB 'a:100' ziel:ifuiyg3qwruqiweidwgh2rhb b:ifuiyg3qwruqiweidwgh2rhb 'b:24' ziel: size=232 len=24 off=0 wid=1 quelle: size=184 len=24 off=0 wid=1 ziel: size=232 len=24 off=0 wid=1 cat: size=536 len=48 off=0 wid=1 cat:ifuiyg3qwruqiweidwgh2rhbifuiyg3qwruqiweidwgh2rhb 'c:536'-d:48- file: size=1032 len=96 off=0 wid=1 file:ifuiyg3qwruqiweidwgh2rhbifuiyg3qwruqiweidwgh2rhbifuiyg3qwruqiweidwgh2rhbifuiyg3qwruqiweidwgh2rhb 'e:1032'-f:96- 1056 1048 64 32
Es gibt recht viele Makros stringxxx(), die alle den Bezeichner eines String-Objektes als einziges Argument benötigen. Makros mit eingeklammertem Inhalt sind read-only. Die anderen sind lesbar und beschreibbar. Diese Makros sind vielfältig in den Funktionen eingesetzt.
stringbuf( name)
Die Basisadresse des angelegten Arrays, die ja in einen Pointer
kopiert wurde, wird um einen solchen Betrag erhöht, so daß die Adresse
nun auf den Beginn des Puffers zeigt, und nicht mehr auf die size-Komponente.
stringfub( name)
Die Operation dieses Makros macht die Aktion des Makros stringbuf()
rückgängig.
Anwendung nur nach einer Anwendung von stringbuf() !
stringsize(name)
Repräsentation der Größe des angelegten Arrays in Byte (size).
stringlen( name)
Aktuelle Elementelänge des Inhalts des Arrays (len).
stringoff( name)
Aktueller Elementeoffset des Arrays (off).
stringwid( name)
Aktuelle Elementbreite des Arrays in Byte (wid).
stringshft(name)
Aktuelle Elementschiebebreite des Arrays in Byte (shft).
stringswid(name)
Repräsentation des Byte swid.
stringcntl(name)
Repräsentation des Byte cntl.
stringfilc(name)
Repräsentation des Byte filc.
stringmag1(name)
Repräsentation des Byte mag1.
stringmag2(name)
Repräsentation des Byte mag2.
stringbufsz(name)
Die Größe des Puffers in Byte (buf[]).
Das hier gezeigte flexible Array aus der Struktur hat keine Größe, sondern
der per Pointer-Casting unterlegte Teil des angelegten Arrays.
stringlenmax(name)
Die maximal mögliche Elementelänge für len.
Funktionen mit Unterstrich string_xxx() sind (eher) für internen Gebrauch. Die anderen stringxxx() sind typische Anwenderfunktionen.
Alle Funktionen, die ein String-Objekt bearbeiten sollen, prüfen das Objekt auf NULL-Pointer und vorhandene MAGIC-Nummern (MAG1=19, MAG2=236). Die MAGICs liegen auf den Objektadressen 8 und 15. Entweder der Prozeß bricht wegen SIGSEGV ab, oder weil nicht beide MAGICs passen. Es ist kaum vorstellbar, daß beide MAGICs bei einem Objekt passen, das kein initialisiertes String-Objekt ist.
Wenn der Datei-Zeiger einer leeren Datei mittels
lseek()
auf eine höhere Position bewegt wird und
auf diese Position geschrieben wird, wird bis zu dieser Position
mit 0 aufgefüllt, oder beim Lesen verhält es sich so, als ob.
Dieses Verhalten wurde fast genau so in die Funktionen
stringsetlen() und
stringsetoff() implementiert.
Diese Funktionen füllen tatsächlich ein entstandenes Loch mit dem
Füllzeichen FILC2, sofern
die Steuerbits
Mit diesen Funktionen können die Puffer gezielt abschnittsweise mit
verschiedenen filc gefüllt werden.
Dies ist allerdings ein Nebennutzen.
Nach Änderung der Länge len zu einem größeren Wert, befindet sich zwischen
dem alten und dem neuen Wert eine Lücke mit (eventuell) uninitialisierten
Daten. Dieses Loch wird aufgefüllt.
Beim Offset off entsteht ein solches Loch, wenn dieser
über die Länge hinaus vergrößert wird.
Die Datenlücke befindet sich dann zwischen dem Längenwert len
und dem neuen Wert des Offsets.
Diese Fehlerfunktion kehrt nicht zurück, weil an deren Ende die Funktion
exit() unbedingt aufgerufen wird, die
ebenfalls nicht zurückkehrt. Dies wird dem Compiler
durch _Noreturn angezeigt, wodurch der deren Aufrufe nach
der letzten oder einzigen ret-Instruktion einsetzt.
Dadurch wird Aufwand gespart, wegen der freizügigen Verwendung von Registern, deren
Inhalte nicht abgesichert und zurückgeholt werden müssen.
Bei Fehlern muß der Prozeß beendet werden, da der jeweils
nachfolgende Code von einwandfreier Operation der vorhergehenden
Funktionen ausgehen muß.
Der Pufferinhalt eines String-Objekts wird null-terminiert, um
stringbuf(dst) an einen Code übergeben zu können, der
einen null-terminierten String oder einen null-terminierten Array-Inhalt erwartet.
Es werden wid 0-Bytes geschrieben.
Diese Funktion macht den Unterschied zwischen null-terminierten
Zeichenketten und den hier vorliegenden Strings deutlich.
Die String-Länge (len) kann beliebig oft hintereinander zwischen
0 und Maximum geändert werden, ohne daß der String in seinem
Puffer inhaltlich geändert wird.
Lesevorgänge berücksichtigen diesen Wert, indem sie entsprechend
viel oder wenig oder keine Daten aus dem Puffer lesen.
Siehe auch oben eine Detail-Beschreibung (Loch) zu dieser Funktion.
Diese Funktion setzt den Offset (off) und verändert die
Länge (len) entsprechend.
Die Länge wird maximal auf den Wert 0 verringert.
Von anderen Funktionen wird ab dem Offset gelesen und geschrieben.
Siehe auch oben eine Detail-Beschreibung (Loch) zu dieser Funktion.
Diese Funktion setzt die Elementbreite (wid) des String-Objekts und berechnet
die Parameter len, off und shft entsprechend neu.
Durch Vergrößerung der Breite entfällt ein zuvor eventuell vorhandener Divisionsrest.
Ein String-Objekt kann mit dieser Funktion auf drei Arten gesetzt werden:
Falls str nicht Null ist, ist widval die Elementbreite
von str und nval
zeigt die Anzahl Elemente an, die ab Offset (dst) kopiert werden sollen.
Hat nval seinen maximalen Wert ~(CTLTYP)0, muß str
null-terminiert sein.
Falls str Null ist, wird der Wert in widval nach
dst kopiert, nval-mal.
Die Elementbreite wid des Zieles dst wird berücksichtigt.
Es wird derjenige niederwertige Teil von widval, der zur Zielbreite paßt, kopiert.
Die Elementbreite der Quelle str darf nicht größer sein, als die Breite
des Zieles.
Retourniert wird die Anzahl kopierter Elemente.
Die Inhalte der Elemente eines String-Objektes werden in ihrer Reihenfolge umgedreht.
Von unten nach oben
wird von oben nach unten.
Ein Offset off, die Länge len und
die Breite wid werden berücksichtigt.
Diese Funktion nimmt nur ein String-Objekt zq entgegen
und erlaubt Kopieraktionen innerhalb dieses Objektes.
Die weiteren Argumente sind die Zielposition zp, die Quellposition qp
und die Anzahl Elemente ne, die kopiert werden sollen.
Es kann von unten nach oben und von oben nach unten kopiert werden.
Auch überlappend.
Bei gleichem Zielwert und Quellenwert oder Anzahl 0 wird sofort still
abgebrochen, denn es gibt in solch einem Fall nichts zu tun.
Vor dem Kopieren berechnet die Funktion viele Fehlersituationen.
0123456789012345678901234567890123456789 0 o=10 l=10 m=30 |---------|---------|---------| n=5 ----z ----z----z----z ----q ----q----q----q z---- z----z----z---- z---- q---- q----q----q---- q----
Vorstehend eine Darstellung als Hilfsmittel für analytische Betrachtungen.
Beim Kopieren nach oben wächst der Verlauf nach unten, und umgekehrt.
Schreiben und Lesen außerhalb des Objektes sind natürlich verboten.
Schreiben unterhalb der Offset-Position ist gesperrt.
Schreiben einer Dateninsel hinter die Länge len, also Erzeugung eines
Datenlochs, ist gesperrt.
Lesen von potentiell uninitialisierten Daten (hinter der Länge) ist gesperrt.
Die Länge len wird nach gültigem Schreiben angepaßt, falls sie größer
als zuvor ist.
Die Positionen müssen ohne Offset off enthaltend
angegeben werden, denn dieser wird intern addiert.
• Ein mit stringstr()
gefundener Substring kann entfernt werden, indem der Inhalt darüber
auf den Anfang des Substrings kopiert wird, und
anschließend die Länge len entsprechend gekürzt wird.
Das Suchmuster in str wird in dem Dateninhalt buf
ab Offset off gesucht.
Die beiden String-Objekte müssen gleiche Breiten wid haben.
In beiden wird der Offset berücksichtigt.
Das Array pos[] informiert über das Suchergebnis und nimmt im ersten
Element pos[0]= p die Position entgegen. Als Startposition
muß 0 gegeben werden. Diese Position wird auch eingesetzt, falls pos
ein Null-Pointer ist.
Nach Rückkehr der Funktion ist die Fundposition in pos[0] enthalten,
oder -1, wenn das Suchmuster nicht gefunden wurde.
Die Weitersuchposition ist in pos[1] enthalten: pos[0]= pos[1],
oder -1, falls ein Weitersuchen sinnlos ist.
Die Länge str:len ist in pos[2] enthalten.
Retourniert wird derselbe Wert, der in pos[0] geschrieben wird (falls
pos kein Null-Pointer ist).
Eine Schleifenbedingung für alle Vorkommnisse kann
so: while (pos[1] > 0) codiert werden.
Testausgaben:
1 5 4 toga....togatog..togatoga...toga... 9 13 4 togatog..togatoga...toga... 18 22 4 togatoga...toga... 22 26 4 toga...toga... 29 -1 4 toga... 1 5 4 toga....togatog..togatoga..toga.... 9 13 4 togatog..togatoga..toga.... 18 22 4 togatoga..toga.... 22 26 4 toga..toga.... 28 32 4 toga.... -1 -1 4
Eine Abwahl des Arrays (pos==0) ist selten sinnvoll. Die Funktion arbeitet prinzipiell binär. Somit können beispielsweise beliebige Dateiinhalte gesucht werden. Die Elementbreite wid kann bis zu 8 Byte betragen. Der Offset off ist in den gelieferten Positionswerten enthalten. Daher kann beispielsweise ein Ausdruck sp->buf.u1 + pos[0] verwendet werden. Im Zusammenhang mit dieser Funktion ist es meist sinnvoll, den Typ STRING* zu verwenden.
Diese Vergleichsfunktion vergleicht nicht einfach nur
unsigned char, sondern Elemente
mit einer Breite wid von 1|2|4|8 Byte.
Die Breite muß bei den beiden String-Objekten ac und bc gleich sein.
Desweiteren ist die Vorzeichenbehaftung beim Vergleich wählbar.
Dazu dient das Steuerbit sign, das ebenfalls bei beiden Objekten
einen gleichen Wert haben muß.
Bei den Breiten 1|2 wird die Differenz
ac - bc retourniert.
Bei den größeren Breiten die Werte +1 oder -1, oder
0 bei Gleichheit.
Ein kürzerer String wird (bei den Breiten 1|2) hinter
seinem letzten Element als der
Wert 0 angenommen: 0-w | w-0 .
Diese Kopierfunktion sorgt dafür, daß das Ziel (dst) nach ihrer Aktion
identisch mit der Quelle (src) ist, mit Ausnahme von size.
Das bedeutet, daß der Zieloffset (off) ignoriert wird!
Es wird eben alles hinter size kopiert.
Kopiert wird mit maximaler Breite AL.
stringcats(ziel, quelle, (void*)0)
kopiert ebenfalls, berücksichtigt aber einen Offset.
In den Puffer des Ziels dst werden ab Offset off die
Inhalte der Quellen src und allen weiteren (...) unter
jeweiliger Berücksichtigung von Länge len und
Offset off hintereinander kopiert (verkettet).
Hinter der letzten Quelle muß das beendende Argument (void*)0 folgen.
Die Elementbreiten wid aller übergebenen Objekte dürfen beliebig
unterschiedlich sein.
Retourniert wird die Anzahl der kopierten Bytes.
Diese Anzahl wird abhängig von der Element-Breite (typ-abhängig) dividiert, um
mit dem Ergebnis die Länge dst:len zu setzen, wobei ein Divisionsrest
gegebenenfalls entfernt wird.
Diese Funktion kopiert kompromißlos schnell.
Es werden alle Alignments 64,32,16,8,4,2 gesucht, die
jeweils bei Ziel und Quelle in Gleichheit vorliegen.
Eine solche Funktion für normale C-Strings (CatS) hat sich in der Praxis
seit 1995 außerordentlich gut bewährt.
Die Werte der Parameter size, len, off, wid des
String-Objektes src werden ausgegeben.
Falls fp Null ist, benutzt die Funktion
stdout, anderenfalls fp.
Argument info darf Null sein und bedarf keiner weiteren Erklärung.
Der Pufferinhalt des String-Objektes src wird ab Offset off ausgegeben.
Die Werte von wid dürfen
1 und 4 (wchar_t) betragen.
Falls fp Null ist, benutzt die Funktion
stdout, anderenfalls fp.
Argument info darf Null sein und bedarf keiner weiteren Erklärung.
Es wird die Anzahl ausgegebener Zeichen retourniert.
Bei Fehler der Ausgabefunktionen wird -1 retourniert.