[C++] bufor i memcpy

0

Witam!
dosc dlugo nie zagladalem na 4p, jednak chcialbym was prosic o pomoc poniewaz napotkalem na problem z ktorym od dwoch dni nie moge sobie poradzic :(
Problem polega na tym, ze w bazie danych w polu typu BLOB zapisuje bufor do ktorego wczesniej wrzucam rozne dane. aby pozniej bylo mozliwe ponowne odczytanie zawartych informacji z ciagu bajtow potrzebuje okreslac np jakiej dlugosci string zostanie zapisany, problem polega na tym ze jezeli mam dlugosc zapisana na 2 bajtach ale, wartosc jest na tyle mala ze wystarczy do jej zapisania jeden bajt, to funkcja mempcy kopiuje do bufora tylko jeden bajt :/
pozniej nie moge tego odczytac poniewaz nie moge zmodyfikowac funkcji odczytujacej bufor.
(tzn moge ale wiazaloby sie to z powaznymi zmianami w calym projekcie na co nie moge sobie pozwolic).

jezeli zmienna add ma wartosc powyzej 2^8 czyli zajmuje wiecej niz 1 bajt a jej typ to unsigned short to oczywiscie funkcja

memcpy(&buffer[size], &add, sizeof(T));

kopiuje do bufora dwa bajty.
Jezeli natomiast wartosc zmiennej add zajmuje tylko jeden bajt to funkcja mempcy kopiuje do bufora jeden bajt. Nie mam pojecia co z tym zrobic poniewaz funkcja odczytujaca z bufora zawsze pobiera pierwsze dwa bajty w celu sprawdzenia jaka dlugosc ma zapisany dalszy ciag znakow.
i prawdopodobnie to jest glowna przyczyna nieprawidlowego dzialania funkcji.

co ciekawe podobno ten kod prawidlowo dziala skompilowany w DevC++
ja uzywam do kompilaci MSVC 2005, probowalem juz na rozne sposoby przerobic ta funkcje ale nic z tego... wypadaloby dodawac ten pierwszy pusty bajt (skladajacy sie z samych zer) jednak jak probuje skopiowac do bufora pojedynczy pusty bajt to nic nie jest dodawane, moze tak ma byc? kopiuje zero to jest nic? :D

ponizej jest funkcja ktora dodaje wartosc danego typu do bufora i tak np moze to byc zmienna x o wartosci 2 typu uint16_t i do bufora dodany zostanie jeden bajt, lub ta sama zmienna o wartosci zajmujacej 2 bajty gdzie do bufora zostanie dodane 2 bajty.

typedef unsigned short uint16_t;

template <typename T>
	inline void ADD_VALUE(T add){
		if((buffer_size - size) < sizeof(T)){
			buffer_size = buffer_size + ((sizeof(T) + 0x1F) & 0xFFFFFFE0);
			buffer = (char*)realloc(buffer,buffer_size);
		}

		memcpy(&buffer[size], &add, sizeof(T));
		size = size + sizeof(T);
	}

Jezeli ktos ma jakies informacje z jakiego podowu tak sie dzieje i czy tak w ogole powinno sie dziac? to bylbym bardzo wdzieczny za informacje. Rowniez pomyslami na rozwiazanie tego problemu nie pogardze ;)

0

Jak zrezygnujesz z szablonu też tak się dzieje?

inline void ADD_VALUE(uint16_t add){
       if((buffer_size - size) < 2){
               buffer_size *= 2;
               buffer = (char*)realloc(buffer,buffer_size);
       }

      memcpy(&buffer[size], &add, 2);
      size +=2;
}

p.s. Rozmiar bufora zawsze zwiększaj mnożąc starą wartość, a nie dodając stałą.

0

zwiekszanie bufora nawet jak jest zle napisane to nie ma to znaczenia w tym momencie poniewaz poczatkowo bufor ma 32 bajty i problem jest przy ciagach krotszych niz 2^8 jak ciag jest dluzszy to memcpy wstawia dwa bajty oznaczajace dlugosc textu i wszystko jest ok.

najpierw dodawany jest 1 bajt oznaczajacy zawsze poczatek, nastepnie maja zostac dodane dwa bajty oznaczajace dlugosc textu i tu jest wlasnie ten problem ze jak wartosc jest mala to dodany zostaje tylko 1 bajt.
sizeof(T) zwraca zawsze 2 jezeli jest to typ unsigned short, jezeli wpisze recznie tak jak Pan zaproponowal nic to nie zmienia, wydaje mi sie ze to moze byc cos z kompilatorem? lub funkcja memcpy?
Kod pochodzi z otwartego projektu i powinien byc poprawny, nic w nim nie modyfikowalem i od poczatku zle dziala :/ a podobno u kolegi ktory kompiluje na DevC++ wszystko dziala.

probowalem tez przesunac miejsce wstawienia bajtu oznaczajacego dlugosc textu o jeden w prawo jezeli wartosc jest zapisana na jednym bajcie, niestety wtedy obcinane jest juz wszystko i zostaje tylko 1 bajt oznaczajacy poczatek.
Teraz tez jest tak ze obcina mi caly text, jezeli mam 1 bajt poczatku, nastepnie jeden bajt oznaczajacy dlugosc, zamiast dwoch bajtow, to obcina mi caly text, dzieje sie tak dlatego ze size zostalo zwiekszone o +2 wiec powstalo wolne miejsce pomiedzy pojedynczym bajtem oznaczajacym dlugosc textu, a wstawianym do bufora textem, powoduje to ze text w ogole nie zostaje wstawiony.

Potrzebuje więc wstawiac przed pojedynczym bajtem oznaczajacym dlugosc tekstu, jakis pusty bajt tak aby wartosc wynosila tyle samo ale byla zapisana na dwoch bajtach, probowalem kopiowac pusty bajt ale nie byl on wstawiany do bufora.

0

A pokaż fragment w którym wywołujesz ADD_VALUE. Jak zweryfikowałeś, że zapisuje się tylko 1 bajt ?
Piszesz, że zmienna 'size' zwiększa się o 2, czyli jest ok. Wiec jak sprawdzasz czy sie zapisało, jak nie zmienną 'size' ?
Jeśli liczba jest mniejsza od 2^8 to starszy bajt jest równy 0, aty piszesz o powstawaniu jakiegoś pustego miejsca - w pamięci nie ma miejsca na pustkę ;).

Wygląda na to, że sypie się przy napotkaniu na 0. Czy przypadkiem nie używasz gdzieś strcpy lub jakiejś innej funkcji operującej na c-stringach do pracy na całym buforze łącznie z tymi pierwszymi bajtami oznaczającymi długość ?

A, i jeszcze ten pierwszy bajt. Ten na samym początku. To jak to z nim jest ?

0

no akurat jak wspomnialem nie ja jestem autorem kodu i sam osobiscie nie jestem dobrym programista wiec nie rozumiem pewnych czesci kodu.

moze zle zrobilem, ale po prostu wyswietlam w konsoli zapytanie ktore wedruje do bazy, jezeli jest jakis bajt to powinienem otrzymac jakis znak? chyba ze bajt zerowy (a to calkiem mozliwe) nie wyswietli zadnego znaku ani odstepu? nawet jezeli to problem polega na tym ze zmienna size bedzie sie zmieniala poniewaz:
size = size + sizeof(T);
ale to nie znaczy ze bufor powiekszyl sie o taka ilosc znakow :/ przynajmniej nie widac tego w konsoli w zapytaniu do bazy.

Nie wiem jak mam to wytlumaczyc.... ale mam bufor z 32 bajtow, na 1 bajcie pojawia sie jakis tam bajt, na dwoch nastepnych ma byc zapisana dlugosc textu ktory jest dodawany na pozostalych bajtach.

teraz jezeli np na 1 bajcie mam ten znak ktory zawsze wystepuje, na drugim wstawie pusty bajt to jezeli na 3 i kolejnych chce cos wstawic to w ogole nie jest dodawane.

jezeli na 2 bajcie zapisuje starszy bajtd dlugosci a na 3 mlodszy bajt dlugosci ciagu to na 4 i kolejnych zapisza sie bez problemu bajty liter z textu.

jezeli zapisuje krotki text np 'aaa' to na 2 bajcie wstawiany jest jakis znak a na reszcie nie ma nic... juz wiecej nic nie jest dodawane do bufora, poniewaz powstala chyba jakas przerwa, bo size zwiekszylo sie o +2 chociaz zostal dodany tylko jeden bajt?
lub jezeli zerowego bajtu nie mozna zobaczyc w ciagu znakow bo odpowiada mu znak ktory nie jest wyswietlany tzn ze memcpy wysypuje sie jezeli bylo dodane 0 :/

moze ktos sprawdzi to u siebie? chociaz jak juz mowilem podobno to dziala prawdilowo, lecz u mnie cos nie chce i nie wiem co jest powodem :/

inline void ADD_STRING(const std::string& add){
		 unsigned short str_len = add.size();

		if((buffer_size - size) < 2){
//tego to do konca nawet nie rozumiem ale jest wywolywane po przekroczeniu 
//bufora 32 bajtow wiec przy krotkich tekstach nie ma znaczenia
			buffer_size = buffer_size + ((2 + 0x1F) & 0xFFFFFFE0); 
			buffer = (char*)realloc(buffer,buffer_size);
		}

		memcpy(&buffer[size], &str_len, 2);
		size = size + 2;

		if((buffer_size - size) < str_len){
			buffer_size = buffer_size + ((str_len + 0x1F) & 0xFFFFFFE0);
			buffer = (char*)realloc(buffer, buffer_size);
		}
		
		memcpy(&buffer[size], add.c_str(), str_len);
		size = size + str_len;
	
	}

strcpy na buforze na pewno nie jest uzywane, inne funkcje to nie wiem, ale raczej nie... wszystko dziala w ten sposob, mam bufor, wywoluje add_string to ma wstawic do bufora 2 bajty oznaczajace dlugosc i reszta bajtow text.

0

Bajt 0 oznacza koniec napisu.
Jeżeli w Twoim buforze jest taki bajt i zrobisz printf(bufor); to zostanie wyświetlony napis tylko do tego znaku.
Jeżeli nie ma takiego bajtu, to program prawdopodobnie się wyłoży, gdyż nie kończysz napisów zerami.
W konsoli nie zobaczysz bajtów zero. Możesz to zrobić edytorem vi(znak ^@)

Np. jeżeli zrobisz sobie plik z 10 zera:
head -c 10 /dev/zero > ./tmp
vi ./tmp
To zobaczysz:
@@@@@@@@@@

Możesz też po prostu sprawdzić, czy (bufor[indeks] == '\0').

P. S.
Radzę też zastąpić
if((buffer_size - size) < str_len)
przez
while((buffer_size - size) < str_len).

0

ok, czyli w takim razie jezeli \0 konczy ciag znakow i za tym juz nic nie jest pokazywane, to by wyjasnialo dlaczego jak sprawdzam w bazie to tez nie widze dalszej czesci...

mozliwe ze caly zapis jest w porzadku tylko mam problem z odczytem :/

dziekuje bardzo ze te kilka uwag :) za chwilke sprawdze pare rzeczy i zedytuje post czy cos sie wyjasnilo :)

@Edit
po odczytaniu pola typu BLOB z bazy danych dlugosc odczytanego ciagu wynosi 2
czyli jest to ten poczatkowy bajt i pojedynczy bajt okreslajacy dlugosc zapisanego tekstu, tyle ze nawet jak zapisuje tekst o dlugosci 0 znakow to do bazy powinno byc zapisane 3 bajty.

zastanawiam sie czy problem przypadkiem nie lezy w bazie danych, jest to sqlite gdzie wszystkie dane przechowywane sa w jednym pliku, po prostu juz sam nie wiem czy to jest wina bazy, kodu, czy kompilatora?

zrobilem jeszcze jeden tescik, mianowicie strlen na buffer:
strlen(buffer) przed dodaniem 2 bajtow dlugosci - 1 <- bajt poczatkowy
strlen(buffer) po dodaniu 2 bajtow dlugosci textu - 2 <- tylko jeden bajt dlugosci textu, ale size+2
strlen(buffer) po dodaniu textu - 2 <- text nawet nie zostaje dodany prawdopodobna przyczyna to opuszczony bajt w buffer poniewaz podczas dodawania informacji o dlugosci textu zostal dodany tylko jeden bajt?, a zmienna size zwiekszyla sie o 2 czyli cos takiego:

buffer[0] = bajt poczatkowy
buffer[1] = bajt dlugosci ciagu
buffer[2] = ???
buffer[3] = proba dodania ciagu znakow na kolejnych bajtach poczynajac od tej pozycji.
i niestety juz w ogole nic nie jest dodawane dalej.

jak dodaje text o duzej dlugosci to sprawa wyglada tak:
buffer[0] = bajt poczatkowy
buffer[1] = starszy bajt dlugosci ciagu
buffer[2] = mlodszy bajt dlugosci ciagu
buffer[3] = znaki ciagu poczynajac od tego bajtu.

w tej sytuacji wszystko jest ok.

nie wiem czy to dobrze ze tak sprawdzam jezeli bajt zerowy jest znakiem konca to moze strlen rowniez nie zwroci odpowiedniej dlugosci?

wyglada na to ze to memcpy po prostu jak skopiuje '/0' to juz wiecej nic nie da sie skopiowac do tego ciagu, opuszczenie w buforze jednego bajtu powoduje ze jest tam wlasnie bajt skladajacy sie z samych zer dlatego tez nie idzie dalej... nie wiem no jak mozna rozwiazac ten problem albo co moze byc przyczyna? jest wlaczona jakas optymalizacja czy cos?

zrobilem cos takiego ze zaraz za 1 bajtem wstawilem '\0'

strlen(buffer) przed dodaniem 2 bajtow dlugosci - 1
strlen(buffer) po dodaniu 2 bajtow dlugosci textu - 1
strlen(buffer) po dodaniu textu - 1

po prostu to zero zakancza wszystko :/ jak sie pojawi zerowy bajt to memcpy nie kopiuje nic wiecej?
dlaczego tak sie dzieje? jak mam sobie z tym poradzic?

jak normalnie, najprosciej w buforze zapisac jakas wartosc skladajaca sie z dwoch bajtow?

0

strlen(bufor) może wskazywać różne rzeczy, gdyż on jest przeznaczony dla napisów z C. Natomiast to, co masz w buforze nie jest napisem z dwóch powodów:
-może mieć '\0' w środku
-nie musi mieć '\0' na końcu

Nie możesz używać takich funkcji jak strlen, strcpy, printf, itp.

Ewentualnie, ponieważ znasz długość napisów, możesz je przekopiować do jakiegoś innego bufora, wpisać '\0' na końcu i dalej operować tym, jak zwykłym napisem.

Coś takiego powinno wypisać zawartość bufora(uwaga, nie testowałem):

void printBuffer(){
 int sz = 0;
 char *b = NULL;
 while (sz < size){
   sz++;//jakis niepotrzebny bajt?
   int dl = ((unsigned short*)(&buffer[sz]))[0];
   sz+=2;
   b = (char*) malloc(dl + 1);
   memcpy(b, buffer[sz], dl);
   b[dl] = '\0';
   printf(b);
   free(b);
   sz+=dl;
 }
}
0

Heh, tibia ;/

Po co robisz strlen(buffer) jak to jest binarne?

To juz rob jakis hexdump buffer..
W size masz dlugosc, i wyswietlaj od buffer[0]...buffer[size]

jak dla mnie kod dodawania stringa:

  • 2 bajty dlugosci STRING.... wyglada ok.
    Moze cos zle zapisujesz/ odczytujesz?

Ale kodu nie chcesz pokazac (btw. kod otserv jest na GPL-2) wiec chyba nic z tym nie zrobimy i musisz sam rozwiazac problem ;/

0

kod od dodawania do bufora jest w porzadku, wybaczcie za to cale zamieszanie, teraz juz wiem o co z tym chodzi... i dziekuje za wytlumaczenie.

problem tkwi albo w bazie... albo w zapytaniu do bazy...
pokombinuje jeszcze i zobaczymy... bo to musi dzialac ;)

1 użytkowników online, w tym zalogowanych: 0, gości: 1