Forum: Compiler & IDEs string mit header versehen ohne kopieren


von 53453453454353 (Gast)


Lesenswert?

hi
hat jemand eine gute idee für folgendes Problem:

Ich habe eine standard sendefunktion die ich aber nur 1x aufrufen darf.
Diese erwartet einen Pointer auf den Datenanfang und eine entsprechende 
Länge.
1
void foo( uint8_t *ptr , uint32_t len )


Nun möchte ich vor dem senden der Daten noch einen Header einbauen.
Kann aber weder vorher speicher reservieren noch den pointer der daten 
verschieben.

bisher wird der datenbereich komplett kopiert.
1
void bar( uint8_t *ptr , uint32_t len )
2
{
3
   uint8_t *buf = malloc( len + 20);
4
5
   // header in neuen buffer und kopieren der daten 
6
   uint32_t new_len = make_header( buf , ptr, len );
7
8
   foo( buf , new_len );
9
10
   free( buf  );
11
}


gibt es eine elegante lösung um den extra buffer zu vermeiden ?
bzw das kopieren zu vermeiden?

quasi eine insertfunktion am anfang des buffers

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

53453453454353 schrieb:
> quasi eine insertfunktion am anfang des buffers

Das liefe auf ein Verschieben des Pufferinhalts hinaus, und das ist 
nicht effizienter, als den Pufferinhalt zu kopieren.

von A. S. (Gast)


Lesenswert?

Nein, gibt es nicht. Typische Workarounds sind
- Sendefunktion mit head und data, je ptr und len.
- Sendefunktion mit oder ohne Abschluss (write und write_line)
- konkrete Headerbehandlung in angepasster Sendefunktion

von Oliver S. (oliverso)


Lesenswert?

Du hast deine Frage doch schon selber beantwortet.

Oliver

von 53453453454353 (Gast)


Lesenswert?

danke ...
dachte vieleicht habe ich etwas übersehen.

Aber es scheint wohl die die einzige lösung zu sein solange man die 
sendefunktion nicht umbiegen kann.
Diese kann ich leider nicht umbauen ohne die ganze Lib dahinter zu 
zerreißen.

von Achim (Gast)


Lesenswert?

53453453454353 schrieb:
> ohne die ganze Lib dahinter zu zerreißen.

Wenn die Strings nicht im ROM sind, kannst Du es auch auf der 
aufruferseie lösen, indem sie vorne Platz für de Header haben.

Auch stört es manchmal nicht, eine Funktion (bzw. ein File) zu überladen

von 53453453454353 (Gast)


Lesenswert?

hi

dasgeht leider nicht weil der string aus einer Lib kommt wo ich wieder 
nichts ändern will/darf

die funktion hat einen pointer als returnwert
das ist dann auch der stringanfang

von S. R. (svenska)


Lesenswert?

Wenn du keine Möglichkeit hast, den Speicher vor dem String zu benutzen 
(wer weiß, was deine Lib da speichert) und du auch keine Möglichkeit 
hast, Header und Daten getrennt zu senden, dann wirst du um eine Kopie 
nicht herumkommen. Punkt.

von 53453453454353 (Gast)


Lesenswert?

S. R. schrieb:
> dann wirst du um eine Kopie
> nicht herumkommen. Punkt.

leider

von Peter D. (peda)


Lesenswert?

memmove() kann ein Array verschieben.

von 53453453454353 (Gast)


Lesenswert?

Peter D. schrieb:
> memmove() kann ein Array verschieben.

wenn dahinter platz ist , ja

da ich aber nur einen pointer habe und nicht weiß wieviel platz hinter 
dem string ist , kann ich das nicht verschieben ohne gefahr zu laufen 
den heap kaputt zu machen

der string kommt aus einer lib die ich nicht allein unter kontrolle habe


deswegen bleibt nur die kopiererei

von Peter D. (peda)


Lesenswert?

53453453454353 schrieb:
> der string kommt aus einer lib die ich nicht allein unter kontrolle habe

Das ist unüblich, dann muß der Aufrufer den String ja wieder freigeben.
Üblich ist, der Aufrufer reserviert den Platz und übergibt den Pointer 
darauf. Dann muß der Aufrufer auch nicht malloc bemühen, sondern legt 
einfach ein lokales Array an.

von 53453453454353 (Gast)


Lesenswert?

Peter D. schrieb:
> Das ist unüblich, dann muß der Aufrufer den String ja wieder freigeben.
> Üblich ist, der Aufrufer reserviert den Platz und übergibt den Pointer
> darauf. Dann muß der Aufrufer auch nicht malloc bemühen, sondern legt
> einfach ein lokales Array an.

ist leider so.
in dieser Lib wird mit malloc speicher geholt.
Je nach stringlänge unterschiedlich groß

Ich bekomme entweder NULL  oder einen gültigen pointer zurück
Wenn NULL sende ich nichts und gebe auch nichts frei.
Wenn gültig muss ichd as senden und entwprechend wieder freigeben.

Ich habe aber die möglichkeit beim senden zu entscheiden ob ich das 
gesammte Paket senden will oder nur teile.
Aktuell war immer das gesamte paket im sendepuffer.
Ich werde das mal testen in dem ich es aufteile und dann versende.

so brauch ich nicht noch einen großen buffer allozieren sondern nur 
einen kleinen den ich neu füllen muss bis das paket weg ist.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Sofern das ganze nicht irrwitzig zeit- und speicherkritisch ist, kannst 
Du Dein Problem mit einer Kombination aus realloc und memmove lösen.

von Peter D. (peda)


Lesenswert?

53453453454353 schrieb:
> in dieser Lib wird mit malloc speicher geholt.
> Je nach stringlänge unterschiedlich groß

Ich weiß eigentlich nie, wie groß ein String letztendlich wird. D.h. ich 
alloziere immer die mögliche Maximalgröße und schreibe dort den String 
rein.

von 53453453454353 (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Sofern das ganze nicht irrwitzig zeit- und speicherkritisch ist,
> kannst
> Du Dein Problem mit einer Kombination aus realloc und memmove lösen.

nunja richtig kritisch nicht.
ist halt ein/mehrere IP Pakete

macht realloc aber nicht genau das?
er holt einen größeren block ( stringlänge + zusatz )
copiert die daten in den neuen und dann Free( alter block )
rückgabewert ist ein neuer pointer des neuen blocks

dieses neue holen einen größeren blocks will ich ja vermeiden :-)

das mache ich ja gerade aktuell ...
ich hole einen neuen block( header + stringlänge )
setze den header und copiere den string rein
dann kann ich den alten block mit string freigeben.

Ich MUSS ja aber zur Laufzeit 2 blöcke aktiv haben

Peter D. schrieb:
> 53453453454353 schrieb:
>> in dieser Lib wird mit malloc speicher geholt.
>> Je nach stringlänge unterschiedlich groß
>
> Ich weiß eigentlich nie, wie groß ein String letztendlich wird. D.h. ich
> alloziere immer die mögliche Maximalgröße und schreibe dort den String
> rein.

Das ist momentan eine künstliche Grenze bei 8Kb
und das mache ich momentan ja so.
Möchte das aber bei bedarf nur so groß machen wie nötig.
bzw nicht doppelt belegen.


na ich seh schon ...
so richtig elegant ohne die Libs zu bearbeiten kommt man nicht 
drumherum.

Einzig das paketweise versenden kann mir noch helfen.
( Websocket mit FIN bit )

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

53453453454353 schrieb:
> dieses neue holen einen größeren blocks will ich ja vermeiden :-)

Realloc holt nicht zwingend einen neuen Block, das tut es nur, wenn sich 
der vorhandene nicht vergrößern lässt.

Warum willst Du das zwanghaft vermeiden? Aus Überzeugung?

Wenn die Rahmenbedingungen so sind, wie von Dir geschildert, /geht es 
nicht anders/.

von 53453453454353 (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Warum willst Du das zwanghaft vermeiden? Aus Überzeugung?


eher aus dem grunde:
aktuell sind es um 4-5kb maximal
es kann aber auch anwachsen


also momentan 4-5Kb als string als returnwert der funktion

dazu ein neuer buffer mit platz für den header  nochmal 4-5kb
zum senden ...


temporär sind das 10kb platz

wenn die daten nun doch noch ansteigen ist es sinnvoll nur die hälfte zu 
verbrauchen

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Wenn Du da ein Problem siehst, rede mit denjenigen, die die beiden 
beiligten Libraries gebastelt haben, auf daß sie das Verhalten ändern.

Geht das nicht, dann geht das nicht, und Du musst halt den Speicher 
entsprechend verarbeiten. Auf was für einem System läuft das, daß Du Dir 
um 10 kB Sorgen machst?

von A. S. (Gast)


Lesenswert?

ich verstehe die Konstrukte mit memmove und realloc nicht. Der String 
der Reinkommt kann damit nicht bearbeitet werden, der zwischenpuffer 
(per malloc geholt) braucht es nicht.

Zu malloc an sich: malloc kann einen segmentierten Speicher 
hinterlassen. Es stellt sich daher m.E. die Frage, ob hier nicht 
vielleicht doch die Maximalgröße auf dem Stack oder statisch allokiert 
werden sollte.

Statisch natürlich nur, wenn diese Funktion niemals aus verschiedenen 
Tasks aufgerufen werden kann.

Auf dem Stack natürlich nur, wenn dieser dafür entsprechende vergrößert 
wird.

Die saubere Lösung ist natürlich immer noch, die eine Sendefunktion der 
Lib um ein Datenfeld zu ergänzen oder in 2 oder 3 aufzuteilen. 
SendeStart(void *data, int len); SendeAppendEnd(void *data, int len); 
Dann braucht es kein malloc, keine Kopiererei.

von Rufus Τ. F. (rufus) Benutzerseite


Lesenswert?

Achim S. schrieb:
> ich verstehe die Konstrukte mit memmove und realloc nicht. Der String
> der Reinkommt kann damit nicht bearbeitet werden, der zwischenpuffer
> (per malloc geholt) braucht es nicht.

Da der "String der reinkommt" mit malloc angefordert wurde, kann auf 
diesen String realloc angewandt werden, um den Puffer zu vergrößern, und 
dann mit memmove der Inhalt so verschoben werden, daß die 
davorzusetzenden Daten halt davor passen.

Wenn die Rahmenbedingungen so sind, daß weder die Routine, die die 
Eingangsdaten liefert noch die Senderoutine geändert werden kann, ist 
das so ziemlich die einzig mögliche Vorgehensweise.

Wenn die Rahmenbedingungen beschreiben würden, daß die Puffergröße ein 
bestimmtes Maximum nicht überschreiten kann, und wenn die 
Rahmenbedingungen ausreichend Platz auf dem Stack vorsehen würden, 
könnte man auch wie von Dir vorgeschlagen den Kram als automatisches 
Array maximaler Größe auf den Stack packen, aber es wird beschrieben, 
daß die im Moment ca. 5 kByte Puffergröße wohl auch noch (irgendwie?) 
wachsen können.

Kann man sich also wohl auch nicht drauf verlassen.


Daß bei Einsatz von malloc & Co. Fragmentierungsprobleme auftreten 
können, ist klar, aber das kann bei den beknackten Rahmenbedingungen 
nicht umgangen werden. Obendrein enthalten die Rahmenbedingungen selbst 
bereits den Einsatz von malloc & Co., da hilft es vermutlich nur wenig, 
hier darauf zu verzichten.

Fazit:
Beknackte Rahmenbedingungen sind beknackte Rahmenbedinungen. Und 
schlechte APIs sind schlechte APIs.

von A. S. (Gast)


Lesenswert?

Rufus Τ. F. schrieb:
> Da der "String der reinkommt" mit malloc angefordert wurde, kann auf
> diesen String realloc angewandt werden, um den Puffer zu vergrößern, und
> dann mit memmove der Inhalt so verschoben werden, daß die
> davorzusetzenden Daten halt davor passen.

Naja, falls Du diese Aussage des UP meinst:

53453453454353 schrieb:
> ist leider so.
> in dieser Lib wird mit malloc speicher geholt.
> Je nach stringlänge unterschiedlich groß

Das habe ich eher als allgemeine Beschreibung gesehen und nicht zwingend 
auf den Anfang des Strings bezogen. Und selbst wenn, wäre ein 
realloc/memmove hier sehr gefährlich und nicht sonderlich performant:

a) es unterstellt, dass der Aufrufer seinen eigenen String nicht mehr 
benutzen darf (er ist vollkommen zerstört)

b) es unterstellt, dass der String beim Aufrufer immer am Anfang des 
Malloc-Bereichs liegt.

c) wenn der Original-Bereich zu klein ist, wird der String zweimal 
kopiert.

Und selbst dann muss die Library geändert werden, da der Aufrufer den 
Speicherbereich für seinen bisherigen String nicht mehr freigeben darf 
!!

von 53453453454353 (Gast)


Lesenswert?

sorry das ich hier so eine lange diskusion ausgelöst habe ^^

die Libs sind leider nicht von mir und teilweise sogar fremde open 
source libs die ich nicht ändern will
der andere Teil die diese lib verwendet ist von kollegen ...

Ändere ich irgendwas schränkt das die wiederverwendbarkeit auf anderen 
Geräten ein.

Wäre es ein hobbyprojekt würde sich die frage garnicht stellen und ich 
hätte das einfach umgebaut.

Ich dachte nur es gibt vlt eine möglichkeit soetwas elegant zu lösen 
ohne einen noch größeren speicher zusätzlich  zu verwenden.


abgesehen vom eigenmächtigen Ändern ... oder das senden der daten in 
mehreren chunks bleibt keine möglichkeit.



Ich danke nochmal  allen beteiligten für die hinweise/hilfe

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.