Gzip
Incolla gzip, zlib o deflate grezzo in Base64 per leggerne il contenuto (i byte dicono quale), o comprimi un testo in uno dei tre, nel browser.
[
{
"name": "Giulia",
"city": "Roma"
},
{
"name": "Leonardo",
"city": "Milano"
},
{
"name": "Sofia",
"city": "Napoli"
},
{
"name": "Francesco",
"city": "Torino"
},
{
"name": "Aurora",
"city": "Firenze"
},
{
"name": "Alessandro",
"city": "Bologna"
},
{
"name": "Ginevra",
"city": "Venezia"
},
{
"name": "Lorenzo",
"city": "Palermo"
}
]Letto come gzip.
- Byte non compressi
- 430
- Byte compressi
- 167
- Variazione
- -61,2%
- Caratteri Base64
- 224
Una compressione in tre involucri, e la parola deflate
gzip, zlib e deflate grezzo sono una sola compressione, DEFLATE, avvolta in tre modi. La RFC 1951 definisce DEFLATE in sé: i byte divisi in blocchi, ciascuno scritto come codici che stanno per un byte o per una sequenza copiata da più indietro. L’involucro di gzip, la RFC 1952, mette davanti un’intestazione di almeno dieci byte e dietro otto byte, un CRC-32 dei byte originali e la loro lunghezza; quello di zlib, la RFC 1950, mette due byte davanti e un Adler-32 dietro; il deflate grezzo è la compressione senza alcun involucro. I dati compressi al loro interno possono essere gli stessi in tutti e tre, quindi l’involucro è tutta la differenza.
È sulla parola deflate che i nomi si aggrovigliano. Per HTTP, Content-Encoding: deflate indica l’involucro di zlib, e la RFC 9110 osserva che alcuni server mandano comunque deflate grezzo sotto quel nome. Il compressore del browser stesso segue HTTP: dà il nome deflate all’involucro di zlib, e il nome deflate-raw al deflate grezzo. DeflateStream di .NET e gzdeflate di PHP scrivono deflate grezzo, mentre gzcompress di PHP e zlib.compress di Python scrivono l’involucro di zlib. Così dei byte etichettati deflate possono essere l’uno o l’altro, e solo i byte possono dire quale.
Per questo la pagina, quando decomprime, legge l’involucro dai byte, e sotto ciò che ne è uscito dice quale ha letto: «Letto come gzip», «Letto come zlib» o «Letto come deflate grezzo». A decidere sono i byte: gzip inizia sempre con 1f 8b, i primi due byte di zlib, letti insieme come un unico numero, sono un multiplo di 31, e nessun codificatore fa iniziare il deflate grezzo in nessuno dei due modi. Un file chiamato .gz che contiene zlib viene letto come zlib, con un avviso che il nome e i byte non concordano, e i byte che non sono in nessuno dei tre vengono rifiutati, perché non c’è niente da decomprimere. Quando comprimi, l’involucro lo scegli tu, ed è gzip finché non ne scegli un altro.
Da dove arrivano i payload, e che cosa sono H4sI ed eJ
I byte compressi arrivano a uno sviluppatore in poche forme: come corpo di una risposta HTTP il cui Content-Encoding nomina gzip o deflate, come file, o come testo, scritto in Base64 o in esadecimale. Ogni flusso gzip inizia con gli stessi tre byte, 1f 8b 08 — i suoi due byte di firma, poi il numero del suo metodo di compressione, che è DEFLATE — e tre byte sono esattamente quattro caratteri di Base64, quindi gzip scritto in Base64 inizia sempre con H4sI. È ciò che una sottoscrizione di CloudWatch Logs consegna a una funzione Lambda o a un flusso Kinesis nei dati di ogni record, ed è così che Helm conserva una release: il suo JSON compresso con gzip, poi scritto in Base64. Quando Helm tiene una release in un Secret di Kubernetes, leggerla direttamente dal Secret dà Base64 dentro Base64, perché un Secret conserva già i suoi dati in Base64 per conto suo; lo strumento Base64 decodifica quello strato esterno e restituisce l’H4sI… che questa pagina legge.
I due byte di intestazione di zlib indicano il suo metodo, la sua finestra e il livello a cui è stato scritto, quindi con la finestra predefinita di zlib il suo Base64 inizia in uno di quattro modi: eJ al livello predefinito, cioè ciò che scrive zlib.compress di Python se non gli si chiede altro, eA o eF ai livelli al di sotto di esso, ed eN a quelli al di sopra. Il deflate grezzo non ha alcuna firma, e il suo Base64 inizia come capita al suo primo blocco.
«Compresso come» dice come la pagina legge e scrive quei byte come testo: «Base64», come all’apertura della pagina, o «Esadecimale». Vale in entrambe le direzioni e la pagina non lo cambia mai al posto tuo, perché ogni cifra esadecimale è anche un carattere di Base64, quindi 1f8b0800 è testo in entrambi e byte diversi in ciascuno. Base64 viene letto nell’alfabeto standard o in quello sicuro per URL, con o senza il suo riempimento, ignorando spazi e a capo, e viene scritto con il riempimento, oppure sicuro per URL e senza riempimento quando l’interruttore «Sicuro per URL» è attivo. L’esadecimale viene scritto in coppie separate da spazi e letto con gli spazi o tutto attaccato, con 0x davanti, o come dump esadecimale — e 0x è il modo in cui T-SQL scrive un valore binario, come il gzip che restituisce COMPRESS() di SQL Server. Quando dell’esadecimale letto come Base64 non va a buon fine, o del Base64 letto come esadecimale viene rifiutato a un carattere che l’esadecimale non ha, e in entrambi i casi tutto il testo si legge nell’altro modo, un avviso lo dice accanto a un pulsante che passa all’altro; nulla cambia finché non lo premi.
Leggere un flusso: i membri, i byte che lo seguono, una fine anticipata
La RFC 1952 fa di un file gzip una serie di membri, flussi gzip completi uno dopo l’altro, ciascuno con la propria intestazione e la propria coda. cat a.gz b.gz produce un file del genere, e lo stesso fa aggiungere a un file con gzip -c file >> archive.gz, l’esempio del manuale stesso di GNU gzip; gunzip legge ogni membro e unisce ciò che contengono. Questa pagina li legge allo stesso modo: ogni membro viene letto e verificato, ciò che contengono si vede unito, e un avviso dice quanti ne sono stati letti. Non tutti i decompressori lo fanno. Lo standard che segue il decompressore del browser stesso concede a un flusso gzip un solo membro e considera un errore il secondo, e zlib.decompress di Python si ferma dopo il primo senza dire nulla.
I byte dopo la fine che non aprono alcun membro vengono saltati invece che rifiutati, perché tutto ciò che li precede è completo e verificato, e un avviso dice quanti sono, lo scostamento da cui iniziano e se sono tutti zero. Lì gli zeri sono riempimento — il manuale di GNU gzip li incontra sui nastri magnetici, scritti fino alla fine di un blocco — e gunzip li salta in silenzio; salta gli altri byte con un avvertimento che dei dati superflui alla fine sono stati ignorati, mentre gzip.decompress di Python rifiuta il file. Lo stesso vale dopo l’Adler-32 di un flusso zlib, e dopo l’ultimo blocco di un flusso grezzo, anche se il deflate grezzo non porta alcun checksum da verificare.
Un flusso che finisce prima della sua fine, come un testo incollato troncato o un download interrotto a metà, viene mostrato fin dove arriva, con un avviso che il checksum non è stato verificato. Un flusso danneggiato viene rifiutato, e non se ne mostra nulla. È più severo di gunzip, che scrive ciò che ha decompresso prima di arrivare al checksum che lo scopre sbagliato; ma un bit cambiato può decodificarsi in silenzio per un tratto prima che qualcosa lo riveli, quindi ciò che è uscito prima che il danno si vedesse potrebbe già essere sbagliato. Non viene indicata nemmeno una posizione, perché il punto in cui un decodificatore si accorge del danno non è quello in cui il danno si trova. E un flusso zlib che chiede un dizionario predefinito viene rifiutato con una frase tutta sua: il flusso identifica i byte con cui il suo compressore è stato inizializzato e non li contiene, quindi non c’è niente con cui leggerlo.
Ciò che esce viene scritto come dice «Byte come»: come «Testo», in UTF-8, o come «Esadecimale». Spesso è JSON, che il Formattatore JSON impagina e verifica, indicando la riga e la colonna in cui si rompe. Un’immagine o un archivio compressi con gzip non sono testo, quindi con «Testo» ogni sequenza dei loro byte che non è UTF-8 appare come U+FFFD, con un avviso che conta quelle sequenze accanto a un pulsante che mostra i byte in esadecimale; «Scarica» salva i byte stessi in entrambi i casi. Una riga sotto il risultato mostra ciò che memorizza l’intestazione del primo membro — un nome, un’ora in UTC e un commento — e il nome memorizzato non dà mai il nome al file che «Scarica» salva. Un risultato che supera il massimo che la pagina decomprime in una volta si ferma lì, con un avviso che indica quella dimensione, e quando la coda di un gzip indica una lunghezza che lo supera, la pagina lo dice mentre il lavoro è in corso.
Perché un input piccolo cresce, e che cosa aggiunge Base64
La compressione guadagna trovando ripetizioni, e un testo breve ne ha poche, mentre ogni involucro costa byte propri: l’intestazione e la coda di gzip arrivano a 18 byte quando l’intestazione non memorizza nient’altro, quelle di zlib a 6, e il deflate grezzo non ne ha, anche se DEFLATE spende qualche bit per segnare i suoi blocchi. Così Hello, world!, 13 byte, diventa 33 byte come gzip, 21 come zlib e 15 come deflate grezzo. La riga delle dimensioni sotto un risultato conta i byte da ciascun lato e la variazione tra i due, +153,8% per quel gzip, e un avviso dice perché quando un risultato cresce. Un testo con molte ripetizioni, come un JSON o un log, recupera quel costo molte volte: l’esempio con cui si apre la pagina, un piccolo JSON, esce a meno della metà della sua dimensione.
Scritti come testo, i byte costano ancora di più. Base64 prende quattro caratteri ogni tre byte, un terzo in più, come spiega la guida dello strumento Base64, quindi quei 33 byte di gzip sono 44 caratteri; l’esadecimale prende due cifre per byte, e la pagina le scrive in coppie separate da spazi, 98 caratteri per gli stessi 33 byte. La riga delle dimensioni conta anche quei caratteri, e il Convertitore di dimensioni dati trasforma qualunque suo conteggio in KB o KiB.
Comprimere: nessun livello, non i byte di gzip -c, e niente Brotli
Comprimere è il lavoro del browser stesso, tramite il CompressionStream che lo standard Compression definisce, e quello standard non offre alcun livello di compressione, quindi non lo offre nemmeno questa pagina: ciò che ottieni è il livello predefinito del browser. gzip -9 può venire un po’ più piccolo, e raramente di molto.
Né i byte sono quelli che scrive gzip -c, anche se entrambi si decomprimono nello stesso testo. Prima di tutto differiscono le intestazioni. GNU gzip memorizza il nome e l’ora di un file a meno che non gli si dica -n, e da una pipe un’ora pari a zero; segna in un byte dell’intestazione quale livello gli è stato chiesto, -9 o -1; e nel decimo byte, che la RFC 1952 assegna al sistema operativo, scrive 03 su Linux, il numero di Unix. Al browser vengono passati solo i byte, quindi né il nome del tuo file né la sua ora possono uscire insieme al risultato: Chromium non memorizza alcun nome e mette l’ora a zero, che è ciò che sceglie gzip -n, e nel decimo byte scrive 03 su Linux, mentre Chrome su Windows scrive 0a. Poi differiscono i dati compressi, perché il compressore di GNU gzip non è quello del browser: su un testo lungo i due scelgono byte diversi allo stesso livello. Una differenza rispetto a gzip -c quindi non significa nulla di per sé; ciò che conta è che entrambi si decomprimano negli stessi byte.
Brotli qui non c’è, per ora. Lo standard Compression lo nomina, ma non tutti i browser lo scrivono ancora, e una scelta che la pagina potesse offrire solo dove il browser la consente renderebbe la pagina diversa da un browser all’altro. zstd non è affatto in quello standard. Ciò che questa pagina legge e scrive è DEFLATE nei suoi tre involucri, e nient’altro.
Un file in entrata, un file in uscita, e niente caricato
Un file può prendere il posto della casella di testo in entrambe le direzioni: scegline uno, oppure trascinalo sulla casella. I suoi byte vengono letti così come sono, senza passare per «Compresso come» né per «Byte come», e qui un file non ha alcun limite, mentre la casella di testo ne ha uno, anche se un file molto grande fa comparire un avvertimento che il browser potrebbe non riuscire a tenerlo in memoria. Quando decomprimi, «Scarica» dà al risultato il nome che gli dà gunzip: il nome del file senza .gz, con .tgz che diventa .tar, e anche senza .zz o .deflate, le estensioni che questa pagina scrive per gli altri due involucri; qualunque altro nome, e qualunque cosa incollata, esce come bytes.bin. Quando comprimi, aggiunge .gz, .zz o .deflate al nome del file, .zz essendo il modo in cui pigz chiama zlib, o alla parola text per ciò che hai digitato.
«Usa come input» porta un risultato nella casella e inverte la direzione, così un’andata e ritorno richiede un solo clic, e un risultato che veniva da un file, o che è troppo grande per la casella, passa come file. In qualunque direzione vada, il file viene letto in questa scheda e il lavoro gira in un Web Worker che la pagina avvia sul tuo dispositivo: né un payload né un file vengono caricati per farlo.
Lo stesso con gunzip e Python
Da riga di comando, gunzip e zcat leggono gzip come fa questa pagina, ogni membro compreso, una volta che base64 -d ha riportato un payload a byte, anche se nessuno dei due legge zlib o deflate grezzo, che entrambi rifiutano come non gzip; gzip -k comprime un file e tiene l’originale accanto. In Python, gzip.decompress legge l’involucro di gzip e ogni membro che contiene, e zlib.decompress legge uno qualsiasi dei tre involucri, quello che gli indica il suo wbits:
# Un payload: decodificarlo, poi decomprimerlo
printf 'H4sIAAAAAAAAA/NIzcnJ11Eozy/KSVEEAObG5usNAAAA' | base64 -d | gunzip # Hello, world!
# Un file: comprimerlo tenendo l’originale, poi stamparlo di nuovo
printf 'Hello, world!' > hello.txt
gzip -k hello.txt
zcat hello.txt.gz # Hello, world!
# Due membri, il secondo aggiunto al primo, riletti come uno solo
printf 'Hello, ' | gzip > two.gz
printf 'world!' | gzip >> two.gz
zcat two.gz # Hello, world!
# Lo stesso in uno script: ogni membro, poi un involucro alla volta
import base64, gzip, zlib
two = open('two.gz', 'rb').read()
zl = base64.b64decode('eJzzSM3JyddRKM8vyklRBAAgXgSK')
raw = base64.b64decode('80jNycnXUSjPL8pJUQQA')
gzip.decompress(two) # b'Hello, world!'
zlib.decompress(two, wbits=31) # b'Hello, '
zlib.decompress(zl, wbits=15) # b'Hello, world!'
zlib.decompress(raw, wbits=-15) # b'Hello, world!'
zlib.decompress(zl, wbits=47) # b'Hello, world!'Con wbits, 31 chiede l’involucro di gzip, mentre 15, il predefinito, chiede quello di zlib, e -15 chiede il deflate grezzo, essendo il 15 di ciascuno la finestra più grande; 47 invece accetta quello di gzip o quello di zlib, a seconda di quello con cui iniziano i byte. zlib.decompress però legge un solo membro e lascia cadere il resto senza dire nulla, ed è per questo che dai due membri qui sopra restituisce solo b'Hello, '; gzip.decompress li legge tutti. Python è anche più severo di questa pagina su due punti: gzip.decompress rifiuta i byte dopo la fine che non sono zeri, ed entrambe le funzioni rifiutano un flusso che finisce prima della fine, dove la pagina mostra ciò che ne è uscito.
Domande frequenti
- Perché il mio risultato compresso è più grande di ciò che ho digitato?
- Perché ogni involucro costa byte propri e un testo breve ha poco che la compressione possa togliere: gzip aggiunge 18 byte, zlib 6, e DEFLATE in più segna i suoi blocchi, quindi poche parole crescono dove un JSON lungo si restringe, e la pagina lo dice in un avviso. Il deflate grezzo è il più piccolo dei tre. Scritto in Base64, qualunque risultato è ancora un terzo più lungo.
- Perché l’output non corrisponde a gzip -c?
- Niente promette che corrisponda. Un’intestazione gzip può contenere un nome, un’ora, un segno del livello e un byte per il sistema operativo, che GNU gzip e il browser riempiono in modo diverso, e i due sono compressori diversi, quindi su un testo più lungo possono differire perfino i dati tra intestazione e coda. Due flussi gzip dello stesso testo sono entrambi giusti quando ciascuno si decomprime in quel testo, quindi confronta ciò in cui si decomprimono: «Usa come input» rimanda subito indietro il risultato di questa pagina.
- Che cosa significa H4sI all’inizio di un payload?
- Che il payload è gzip scritto in Base64: ogni flusso gzip inizia con i byte 1f 8b 08, e quei tre byte sono H4sI in Base64. Incollalo qui così com’è. Un payload che inizia con eJ è molto probabilmente zlib al suo livello predefinito, e uno che non inizia con nessuno dei due può essere deflate grezzo, o non essere affatto compresso, cosa che la pagina ti dice.
- gzip è una forma di crittografia?
- No. La compressione non usa alcuna chiave, quindi chiunque abbia i byte può decomprimerli, qui o con gunzip, e ogni byte torna com’era. Una password dentro un payload compresso con gzip è esposta quanto una scritta per intero.
- Il mio payload o il mio file vengono caricati da qualche parte?
- No. Il payload che incolli e il file che scegli o trascini vengono letti entrambi dentro questa scheda, dove un Web Worker avviato dalla pagina si occupa della compressione e della decompressione, e «Scarica» costruisce il suo file dal risultato proprio lì. Nessuno dei due viene inviato a questo sito o a chiunque altro.
Strumenti correlati
- Formattatore JSON
Valida e abbellisce il JSON, con posizioni di errore chiare.
- Convertitore di dimensioni dati
Converti fra KB, MB, GB e KiB, MiB, GiB.
- Base64
Codifica e decodifica Base64 — supporto UTF-8 completo.
- Base32
Codifica e decodifica Base32 e base32hex — testo in UTF-8 o byte in esadecimale.