Understanding byte arrays
The five ways languages spell "a sequence of bytes".
Uint8Array, Buffer, bytes, []byte, byte[] — the same idea in five typeface flavours, plus the conversions between them.
Bytes are bytes everywhere.
A byte is 8 bits, 0-255. An array of bytes is the universal building block of network packets, file contents, cryptographic primitives, and image pixel data. Every mainstream language has a type for it — but the names and ergonomics differ wildly. The values are the same; the surface area is what changes.
The five spellings.
JavaScript: Uint8Array (typed array view over an ArrayBuffer) — orBuffer in Node.js, which is a Uint8Array subclass with extra methods. Python: bytes (immutable) and bytearray (mutable). Go:[]byte, an ordinary slice. Java: byte[] — signed (-128 to 127), which surprises everyone who ever XORs anything. Rust: Vec<u8>(owned) or &[u8] (borrowed slice). C/C++: unsigned char *or std::vector<uint8_t>.
Hex and base64 are the wire formats.
When bytes have to traverse a string-only medium — JSON, logs, environment variables, a copy-paste — they get encoded to text. Hex doubles the size (2 chars/byte) and is unambiguous. Base64 inflates by 33 % and is the standard for JSON-embedded binary. Base32 inflates by 60 % but is human-typeable. The byte-array tool's job is moving between these representations without surprises about the byte values themselves.
A worked conversion.
Three bytes 0x48 0x49 0x21: hex "4849 21", base64 "SEkh", base32 "JBEKC===", ASCII "HI!", decimal array[72, 73, 33]. All five views show the same underlying bytes. The choice of representation depends only on the medium: copy-paste into Slack → hex; embed in JSON → base64; render as text → UTF-8 decode.
"HI!" five ways
3 bytes, 5 representations
Same bytes, different encodings.
48 49 21 ; SEkh ; JBEKC=== ; [72,73,33] ; HI!
= Pick by medium
The signed-byte gotcha.
Java's byte is signed: 0xFF is -1, not 255. When you XOR two bytes, the result is an int, and 0xFF becomes 0xFFFFFFFF after sign extension. Always mask with & 0xFF when reading byte values into int arithmetic. C and Go are unsigned by default for byte-array types. Python's bytes indexes to int (unsigned). JavaScript's Uint8Array is unsigned. The Java footgun is unique enough to remember.
Slicing rules.
In Python, bytes[2:5] copies. In Go, b[2:5] shares the underlying array. In JavaScript, arr.subarray(2, 5) shares;arr.slice(2, 5) copies. In Rust, &v[2..5] borrows. The mutability story changes too: modifying a Go sub-slice mutates the parent; modifying a Python sub-bytes does not (bytes are immutable, and bytearrays slice into separate objects). Knowing which behaviour applies prevents silent corruption.