Hlavní navigace

GLib: Užitečnosti všeho druhu (2)

18. 5. 2001
Doba čtení: 6 minut

Sdílet

Dnes nás kromě popisu několika dalších drobných utilit čeká i povídání o tom, co GLib dělá pro to, aby programy napsané v Unixu šlo zkompilovat i ve Windows.

Další utility

gint g_bit_nth_lsf(guint32 mask, gint nth_bit);

…vrátí pozici (index) prvního nastaveného (1) bitu v masce mask, hledáno od bitu s pořadím nth_bit nahoru (ale ne včetně). Bity jsou číslovány od 0 (dolní bit, Least SigniFicant) po 31. Chcete-li prohledávat bity od prvního (s indexem 0), vložte za nth_bit hodnotu –1.

gint g_bit_nth_msf(guint32 mask, gint nth_bit);

…dělá totéž, ale od nejvyššího bitu (Most SigniFicant) dolů. Indexy bitů jsou stejné, tedy od 0 (nejspodnější bit) po 31. Chcete-li začít prohledávání od nejvyššího bitu (s indexem 31), vložte za nth_bit číslo 32 nebo –1.

guint g_bit_storage(guint number);

…vrátí počet bitů nutných k uložení čísla number. Např. pro číslo 4 jsou potřeba 3 bity (4 binárně = 100).

guint g_spaced_primes_closest(guint num);

…vrátí nejmenší prvočíslo z vnitřního pole prvočísel, které je větší než num. Vnitřní pole prvočísel obsahuje prvočísla z intervalu od 11 do 13845163 taková, že každé následující prvočíslo je zhruba 1,5 až dvojnásobkem prvočísla předchozího. Samotná GLib funkci využívá pro stanovení optimální velikosti hash tabulek ( GHashTable).

guint g_parse_debug_string(const gchar *string,
                           GDebugKey *keys,
               guint nkeys);

struct GDebugKey
{
  gchar *key;
  guint  value;
};

Funkci g_parse_debug_string() používají knihovny GDK a GTK k analýze nastavení ladicích výstupů. Parametr string je dvojtečkami ( :) oddělený seznam voleb. Argument keys je pole struktur GDebugKey o velikosti nkeys. Funkce vrátí bitovou mapu sestavenou podle voleb v řetězci string a jím odpovídajícím maskám value. Jinými slovy, v poli keys určíte, jaké volby ( key) mají odpovídat jakým bitům ( value). Je-li v řetězci string hodnota "all", nastaví se všechny volby.

Příklad:

GDebugKey keys[] = {{"warning", 1},
                    {"fatal", 2},
            {"assert", 4},
            {"log", 8}};
guint flags;

flags = g_parse_debug_string("fatal:assert", keys, 4);
  /* vysledkem bude: 2 || 4 = 6 */

flags = g_parse_debug_string("all", keys, 4);
  /* vysledkem bude: 1 || 2 || 4 || 8 = 15 */

Jistě znáte ze standardního C funkci atexit(). GLib pro konvenci definuje její analogii:

void g_atexit(GVoidFunc func);

void(*GVoidFunc)(void);

Funkce g_atexit() zařadí funkci func do seznamu funkcí, které jsou vyvolány při normálním ukončení programu (voláním funkce exit() nebo návratem z funkce main). Funkce takto registrované jsou volány v obráceném pořadí jejich registrace. Nejsou jim předávány žádné argumenty (viz prototyp  GVoidFunc).

Funkce pro kompatibilitu s Windows

Vedle různých podpůrných utilit se knihovna GLib snaží zvýšit přenositelnost programů z Unixů do Windows také dalším způsobem. Pokud své zdrojové kódy kompilujete pod Windows, má knihovna GLib definováno množství maker, které mají stejné názvy jako některé standardní funkce Unixu. Spousta těchto maker jenom jednoduše přejmenovává standardní Windowsoidní funkce. Výsledkem pak je fakt, že zdrojové kódy svých programů nemusíte po změně platformy vůbec měnit.

#define MAXPATHLEN 1024

…maximální délka názvu cesty.

#define NAME_MAX 255

…maximální délka samotného jména souboru.

Následují makra, která suplují standardní funkce Unixu. Jejich funkčnost je naprosto stejná s funkcemi Unixu tohoto jména, proto zde uvádím jenom jejich výčet. Pilný čtenář si jejich vlastnosti může vyhledat v příručkách o programování v operačních systémech Unix.

#define getcwd
#define getpid

typedef pid_t;

#define access
#define open
#define read
#define write
#define lseek
#define close
#define pipe(phandles)
#define popen
#define pclose
#define fdopen
#define ftruncate(fd, size)
#define opendir
#define readdir
#define rewinddir
#define closedir

Big-endian vs. Little-endian

Jak asi víte, některé procesory se od ostatních liší v tom, že fyzicky v paměti uchovávají bity v jiném pořadí. Jedny ukládají horní bajt dříve než dolní – a takovému uspořádání se říká big-endian.

Jiné procesory (např. procesory rodiny x86) zase ukládají horní bajt nakonec. Toto uspořádání je známo pod názvem little-endian.

Pro zkomplikování celé záležitosti, některé procesory ukládají bajty v úplně exotickém pořadí. Bajty 4-bajtového slova uloží v pořadí 3, 4, 1 a 2. Takovému řazení se říká PDP-endian.

Pokud se se svými aplikacemi plácáte jenom na jednom typu procesoru, je vše v pořádku. Problémy nastanou až při komunikaci různých typů procesorů po síti nebo prostřednictvím binárních souborů. V knihovně GLib proto naleznete sadu maker, které realizují konverze mezi jednotlivými indiány.

Zjištění uspořádání bajtů

Začneme nejprve makrem

#define G_BYTE_ORDER

…které prozrazuje, s jakou mašinou vlastně máme tu čest. Podle platformy se makro rozvine na jednu z hodnot:

#define G_LITTLE_ENDIAN
#define G_BIG_ENDIAN

nebo

#define G_PDP_ENDIAN

(rozpoznávání G_PDP_ENDIAN ještě není v GLib implementováno)

Převod mezi ‚host‘ a ‚network byte order‘

V Internetu je standardní uspořádání bajtů známé pod označením ‚network byte order‘. (De facto je to big-endian.) Pro převod na/z něj slouží makra:

#define g_htonl (val)
#define g_htons (val)
#define g_ntohl (val)
#define g_ntohs (val)

Význam maker dekódujete podle následujícího klíče: první písmeno v názvu (za g_) označuje, z kterého prostředí se má dekódovat ( h = host (náš počítač), n = network (síť)), následuje to, a po něm cílové prostředí (opět h nebo n). Poslední písmeno v názvu označuje typ hodnoty val ( l je pro 32-bitový integer (Long) a s pro 16-bitový integer (Short)).

Převod mezi little-endian a big-endian

#define GINT_FROM_BE (val)

…převede gint ovou hodnotu val z big-endian ( BE) na uspořádání použité aktuální platformou.

#define GINT_FROM_LE (val)

…zkonvertuje gint ve formátu little-endian ( LE) na formát pro aktuální platformu.

#define GINT_TO_BE (val)

…transformuje gint na big-endian reprezentaci.

#define GINT_TO_LE (val)

…převede gint val na little-endian.

Stejná makra jako předcházející čtveřice máme i pro ostatní celočíselné datové typy. Pro guint ( unsigned int) jsou to:

#define GUINT_FROM_BE (val)
#define GUINT_FROM_LE (val)
#define GUINT_TO_BE (val)
#define GUINT_TO_LE (val)

Pro glong:

#define GLONG_FROM_BE (val)
#define GLONG_FROM_LE (val)
#define GLONG_TO_BE (val)
#define GLONG_TO_LE (val)

I pro gulong ( unsigned long):

#define GULONG_FROM_BE (val)
#define GULONG_FROM_LE (val)
#define GULONG_TO_BE (val)
#define GULONG_TO_LE (val)

Nebo pro 16-ti bitový int ( gint16):

#define GINT16_FROM_BE (val)
#define GINT16_FROM_LE (val)
#define GINT16_TO_BE (val)
#define GINT16_TO_LE (val)

…atd. (názvy jsou jistě vševysvětlující):

#define GUINT16_FROM_BE (val)
#define GUINT16_FROM_LE (val)
#define GUINT16_TO_BE (val)
#define GUINT16_TO_LE (val)

#define GINT32_FROM_BE (val)
#define GINT32_FROM_LE (val)
#define GINT32_TO_BE (val)
#define GINT32_TO_LE (val)

#define GUINT32_FROM_BE (val)
#define GUINT32_FROM_LE (val)
#define GUINT32_TO_BE (val)
#define GUINT32_TO_LE (val)

#define GINT64_FROM_BE (val)
#define GINT64_FROM_LE (val)
#define GINT64_TO_BE (val)
#define GINT64_TO_LE (val)

#define GUINT64_FROM_BE (val)
#define GUINT64_FROM_LE (val)
#define GUINT64_TO_BE (val)
#define GUINT64_TO_LE (val)

Veškeré konverze mezi big-endian, little-endian i PDP-endian jsou symetrické. Pro konverze mezi jednotlivými uspořádáními můžete používat přímo následující makra, která provádí konverze mezi dvěma typy. Např.

#define GUINT16_SWAP_BE_PDP (val)

…převede guint16 z big-endian na PDP-endian i zpět. Je-li val ve formátu big-endian, výsledkem volání GUINT16_SWAP_BE_PDP(val) bude hodnota v PDP-endian, a naopak, je-li val v PDP-endian, po konverzi tímto makrem se dostane do big-endian reprezentace.

Podobně tak i další makra:

#define GUINT16_SWAP_LE_BE (val)

…provádí konverzi little-endian/big-endian (a zpět) nebo

#define GUINT16_SWAP_LE_PDP (val)

…která realizuje převod little-endian/PDP-endian (a zpět) pro guint16.

Pro integery jiné velikosti jsou zde další odrůdy:

#define GUINT32_SWAP_BE_PDP (val)
#define GUINT32_SWAP_LE_BE (val)
#define GUINT32_SWAP_LE_PDP (val)

a

root_podpora

#define GUINT64_SWAP_LE_BE (val)

To by tak bylo z mé strany pro dnešek vše. U dalších pokračování se těší

Byl pro vás článek přínosný?

Autor článku

Michal Burda vystudoval informatiku a aplikovanou matematiku a nyní pracuje na Ostravské univerzitě jako odborný asistent. Zajímá se o data mining, Javu a Linux.