Forum: Compiler & IDEs string mit header versehen ohne kopieren


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
von 53453453454353 (Gast)


Bewertung
0 lesenswert
nicht 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) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht 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. (achs)


Bewertung
1 lesenswert
nicht 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)


Bewertung
-3 lesenswert
nicht lesenswert
Du hast deine Frage doch schon selber beantwortet.

Oliver

von 53453453454353 (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
1 lesenswert
nicht 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)


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

leider

von Peter D. (peda)


Bewertung
-1 lesenswert
nicht lesenswert
memmove() kann ein Array verschieben.

von 53453453454353 (Gast)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht 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)


Bewertung
0 lesenswert
nicht 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) (Moderator) Benutzerseite


Bewertung
1 lesenswert
nicht 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. (achs)


Bewertung
0 lesenswert
nicht 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) (Moderator) Benutzerseite


Bewertung
0 lesenswert
nicht 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. (achs)


Bewertung
0 lesenswert
nicht 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 
!!

: Bearbeitet durch User
von 53453453454353 (Gast)


Bewertung
0 lesenswert
nicht 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

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.