Am avut recent un proiect pentru un client scandinav. Printre altele a trebuit sa parsez ceva si am folosit isdigit(). Functie antica din libraria standard C. Dar vorba lunga (blogu' lung) saracia omului... isdigit(x) unde x are o valoare din afara intervalului [0,255] se compileaza in ceva ce duce la citirea dintr-o zona de memorie random (probabil neintializata). Cu putin noroc ajungi sa citesti de la adresa 0 :) Asta pentru visual studio 6.0 (cu toate service pack-urile, paca vreo 5?), nu stiu cum o fi la celalalte versiuni de visual studio...
Dovada: isdigit() face parte din biblioteca crt care vine cu tot cu surse in cazul visual studio 6.0. Detalii de implementare pot fi gasite in ctype.c si ctype.h. Pot fi gasite in c:/programme/microsoft visual studio/vc98/crt/src/ si c:/programme/microsoft visual studio/vc98/include/ (presupunind ca msdev a fost instalat in c:/programme/).
Functia isdigit() e definita asa:
inline int isdigit(int _C) {return (_pctype[_C] & _DIGIT); }
unde _DIGIT e:
#define _DIGIT 0x4 /* digit[0-9] */
si _pctype e adresa de start a arrayului de mai jos:
unsigned short _ctype[257] = {
0, /* -1 EOF */
_CONTROL, /* 00 (NUL) */
_CONTROL, /* 01 (SOH) */
_CONTROL, /* 02 (STX) */
_CONTROL, /* 03 (ETX) */
_CONTROL, /* 04 (EOT) */
_CONTROL, /* 05 (ENQ) */
_CONTROL, /* 06 (ACK) */
_CONTROL, /* 07 (BEL) */
_CONTROL, /* 08 (BS) */
_SPACE+_CONTROL, /* 09 (HT) */
_SPACE+_CONTROL, /* 0A (LF) */
_SPACE+_CONTROL, /* 0B (VT) */
_SPACE+_CONTROL, /* 0C (FF) */
_SPACE+_CONTROL, /* 0D (CR) */
_CONTROL, /* 0E (SI) */
_CONTROL, /* 0F (SO) */
_CONTROL, /* 10 (DLE) */
_CONTROL, /* 11 (DC1) */
_CONTROL, /* 12 (DC2) */
_CONTROL, /* 13 (DC3) */
_CONTROL, /* 14 (DC4) */
_CONTROL, /* 15 (NAK) */
_CONTROL, /* 16 (SYN) */
_CONTROL, /* 17 (ETB) */
_CONTROL, /* 18 (CAN) */
_CONTROL, /* 19 (EM) */
_CONTROL, /* 1A (SUB) */
_CONTROL, /* 1B (ESC) */
_CONTROL, /* 1C (FS) */
_CONTROL, /* 1D (GS) */
_CONTROL, /* 1E (RS) */
_CONTROL, /* 1F (US) */
_SPACE+_BLANK, /* 20 SPACE */
_PUNCT, /* 21 ! */
_PUNCT, /* 22 " */
_PUNCT, /* 23 # */
_PUNCT, /* 24 $ */
_PUNCT, /* 25 % */
_PUNCT, /* 26 & */
_PUNCT, /* 27 ' */
_PUNCT, /* 28 ( */
_PUNCT, /* 29 ) */
_PUNCT, /* 2A * */
_PUNCT, /* 2B + */
_PUNCT, /* 2C , */
_PUNCT, /* 2D - */
_PUNCT, /* 2E . */
_PUNCT, /* 2F / */
_DIGIT+_HEX, /* 30 0 */
_DIGIT+_HEX, /* 31 1 */
_DIGIT+_HEX, /* 32 2 */
_DIGIT+_HEX, /* 33 3 */
_DIGIT+_HEX, /* 34 4 */
_DIGIT+_HEX, /* 35 5 */
_DIGIT+_HEX, /* 36 6 */
_DIGIT+_HEX, /* 37 7 */
_DIGIT+_HEX, /* 38 8 */
_DIGIT+_HEX, /* 39 9 */
_PUNCT, /* 3A : */
_PUNCT, /* 3B ; */
_PUNCT, /* 3C < */
_PUNCT, /* 3D = */
_PUNCT, /* 3E > */
_PUNCT, /* 3F ? */
_PUNCT, /* 40 @ */
_UPPER+_HEX, /* 41 A */
_UPPER+_HEX, /* 42 B */
_UPPER+_HEX, /* 43 C */
_UPPER+_HEX, /* 44 D */
_UPPER+_HEX, /* 45 E */
_UPPER+_HEX, /* 46 F */
_UPPER, /* 47 G */
_UPPER, /* 48 H */
_UPPER, /* 49 I */
_UPPER, /* 4A J */
_UPPER, /* 4B K */
_UPPER, /* 4C L */
_UPPER, /* 4D M */
_UPPER, /* 4E N */
_UPPER, /* 4F O */
_UPPER, /* 50 P */
_UPPER, /* 51 Q */
_UPPER, /* 52 R */
_UPPER, /* 53 S */
_UPPER, /* 54 T */
_UPPER, /* 55 U */
_UPPER, /* 56 V */
_UPPER, /* 57 W */
_UPPER, /* 58 X */
_UPPER, /* 59 Y */
_UPPER, /* 5A Z */
_PUNCT, /* 5B [ */
_PUNCT, /* 5C \ */
_PUNCT, /* 5D ] */
_PUNCT, /* 5E ^ */
_PUNCT, /* 5F _ */
_PUNCT, /* 60 ` */
_LOWER+_HEX, /* 61 a */
_LOWER+_HEX, /* 62 b */
_LOWER+_HEX, /* 63 c */
_LOWER+_HEX, /* 64 d */
_LOWER+_HEX, /* 65 e */
_LOWER+_HEX, /* 66 f */
_LOWER, /* 67 g */
_LOWER, /* 68 h */
_LOWER, /* 69 i */
_LOWER, /* 6A j */
_LOWER, /* 6B k */
_LOWER, /* 6C l */
_LOWER, /* 6D m */
_LOWER, /* 6E n */
_LOWER, /* 6F o */
_LOWER, /* 70 p */
_LOWER, /* 71 q */
_LOWER, /* 72 r */
_LOWER, /* 73 s */
_LOWER, /* 74 t */
_LOWER, /* 75 u */
_LOWER, /* 76 v */
_LOWER, /* 77 w */
_LOWER, /* 78 x */
_LOWER, /* 79 y */
_LOWER, /* 7A z */
_PUNCT, /* 7B { */
_PUNCT, /* 7C | */
_PUNCT, /* 7D } */
_PUNCT, /* 7E ~ */
_CONTROL, /* 7F (DEL) */
/* and the rest are 0... */
};
Acu poti sa-ti imaginezi ce se petrece cand apelezi isdigit('Ä') (client scandinav) si (int)'Ä'=-60 (UTF8... beats me, un standard, nu mai stiu care, ala cel mai intilnit). Poti incerca in debugger... poti pune _pctype in watch window, da' nu poti intra in isdigit pen'ca e inlined. Aceeasi poveste pentru islower, isalnum... si restul.
Implementarea e altfel pentru gcc. Nu stiu cum e cu compilerul de la Intel. Cineva mi-a zis ca in visual studio 2005 se testeaza intr-un fel valoarea lui x in versiunea de debug (sa nu iasa din intervalul ala), nu insa si in cea de release.
Oricum, implementarea e probabil cea mai rapida, respect.
8 comentarii:
uaahaahääii.. shci ditamai cîrnatzu' o ieshît!!
Pariu ca o sa fie cel mai lung post de pe blogu asta ? ever.
Ha .. te bati tu cu sinica la asta :-)
Nu se pune (la lungime) postul lui Nicu ca e garnisit cu mult cod luat de microsft :) Vezi ca poate le incalcam copyright-ul sau EULA publicind astfel dumele pe site :)
Treburile astea cu caracterele pot fi mare pacoste:)
Daca stii ca ai de a face cu caractere multibyte sau unicode trebe sa folosesti iswdigit.
De fapt povestea e mai complicata..
Daca caracterul ala e utf-8 (sa luam cazul ca il citesti intr-un fisier codificat cu utf-8) posibil sa trebuiasca sa-l treci oleaca si prin functia MultiByteToWideChar inainte.
Vezi daca ajuta :)
Eh .. sinica s-o lasat dus pe carari de munte cu WideChars :-))
O fost prost ori lenes ala de o scris functia. Putea sa faca ceva de genul;
inline int isdigit(int _C) {return (_pctype[_C & 0xFF ] & _DIGIT); }
si era in regula (tot atat de rapida era :- )).
Daca un api are semntatura isDigit(int) eu pot sa ii trimit linistit isDigit(-1000) si ar trebui sa mearga nu sa imi citeasca RAM-ul.
Mihai
Pai e doua modalitati de a face codu rapid: caching sau caching. Daca ai noroc iti iese si din algoritm dar nu prea des :-). De obicei caching-ul te ajuta la chestii dese. Exemplu clasic de cache cu lookup tables :-). Respect poate prea mult .. good job e mai corect dupa mine.
Mihai
Fiind vorba de isdigit,nu prea ai nevoie ce e drept de ce am spus :) Din cite stiu eu,toate code-pageurile multibyte pastreaza numerele si literele din alfabetul englez la locul lor,dar poate ma insel :)
Fa si tu propia functie "neoptimizata", tot un un drac e daca nu spargi nuci cu programul ala :)
Mihai, buna ideea cu &0xFF. Dar totusi e o operatie in plus. Desi cu procesoarele de azi, cu pipelining... s-ar putea sa nu conteze...depinde de context.
E buna ca eviti ca citesti memoria :-) dar ... se modifica semantica la functie si nu stiu daca e bine. De ex cu smenu ala o sa avem ca isDigit(0x8) == isDigit(x108) care nu e chiar corect :-).
Banuiesc ca asta e compromisul cel bun deoarece orice altceva strica ori una ori alta (desi ar putea sa comenteze si aia prin documentatie ca nu are rost sa dai parametri gresiti ca nu stie ce se intampla).
Mihai
Trimiteți un comentariu