Skip to content

Data

Data is an Object that owns a heap-allocated block of bytes of a given length. Unlike String it adds no trailing NUL and treats the bytes as opaque, with no character-class operations. It is the value type for a raw blob: a file’s contents, a packet, an encoded string.

#import "Foundation.xc" // or #import "Data.xc"

A Data owns a growable byte buffer and a length. It grows geometrically (16, then doubling), so a run of appendByte is amortised O(1) per byte instead of O(n) for reallocating on every byte. capacity reports bytes allocated, length bytes used.

The buffer is a raw u8*, not a class pointer, so Data frees it in dealloc; the automatic aggregate walker does not reclaim raw allocations. bytes returns that pointer for direct access. It stays valid as long as the Data does.

Searches and slices follow the same conventions as String: a search miss is Data.notFound(), and an out-of-range slice clamps to empty rather than faulting.

Data also holds the byte/encoding bridge with String. Both directions live here (withString, stringValue, withStringEncoded) because Data already imports String, and the reverse import would make the two files cyclic.

  • Comparable: compare gives memcmp-order total ordering, so Data can be sorted and used as a key.
  • Hashable: hash (FNV-1a over the bytes) makes a Data a Map / Set key.
  • Copying: copy returns an independent duplicate over the same bytes.

Every Data* is also an Object* and fits anywhere one is expected.

Creating · withBytes · withCapacity · withLength · withData · init

Reading · length · isEmpty · bytes · byteAt · capacity

Mutating · setByteAt · appendByte · append · appendBytes · increaseLengthBy · setLength · reserve

Slicing · subdata · subdataFrom

Searching · indexOfByte · containsByte · notFound

String bridge · withString · stringValue · withStringEncoded

Text · hexString

Protocol methods · description · equals · compare · hash · copy · dealloc


static Data* withBytes(u8* src, u32 len)

Copies len bytes from src into a new heap allocation. The caller’s source pointer can be freed or reused immediately afterwards. This is the usual constructor.

static Data* withCapacity(u32 len)

Reserves room for len bytes but returns an empty Data (length 0); grow it with the append methods. This avoids reallocation while building up to len bytes. It does not pre-fill; use withLength for that.

static Data* withLength(u32 len)

Allocates len zero-filled bytes, with length already len. Use when you want to fill the bytes in place via setByteAt before handing the Data off.

static Data* withData(Data* other)

An independent copy of other’s bytes (same as copy). A null other yields an empty Data.

void init(void)

The default initializer: an empty Data (null buffer, zero length). Prefer the with… factories; you rarely call init directly.

↑ Topics

u32 length(void)

Number of valid bytes. O(1).

bool isEmpty(void)

true when the length is zero.

u8* bytes(void)

A borrowed pointer to the Data’s own buffer, for direct access. It is valid as long as the Data instance is, until the buffer grows: an append may reallocate, after which an earlier pointer dangles.

u8 byteAt(u32 idx)

The raw byte at idx.

u32 capacity(void)

Bytes currently allocated in the backing buffer (≥ length). See reserve.

↑ Topics

void setByteAt(u32 idx, u8 value)

Writes value at idx (in range [0, length)).

void appendByte(u8 b)

Appends one byte, growing the buffer geometrically if needed.

void append(Data* other)

Appends every byte of other. A null or empty other is a no-op.

void appendBytes(u8* src, u32 n)

Appends n bytes from src.

void increaseLengthBy(u32 n)

Grows the length by n zero bytes (Foundation’s increaseLengthBy:). The new bytes are zeroed rather than left with the allocator’s previous contents.

void setLength(u32 n)

Truncates to n bytes, or extends with zeroes (via increaseLengthBy) if n is larger than the current length.

void reserve(u32 need)

Grows the buffer so at least need bytes fit without reallocating, which amortises a known series of appends. A request that already fits does not touch the heap.

↑ Topics

Out-of-range clamps to empty, as String’s slicing does. Each returns a new Data*.

Data* subdata(u32 from, u32 len)

A new Data of len bytes starting at byte from (clamped to what is available).

Data* subdataFrom(u32 from)

Everything from byte from to the end.

↑ Topics

u32 indexOfByte(u8 needle)

First offset of byte needle, or Data.notFound() if absent.

bool containsByte(u8 needle)

true if needle occurs anywhere (indexOfByte(needle) != notFound()).

static u32 notFound(void) // 0xFFFFFFFF

The sentinel returned by indexOfByte on a miss.

↑ Topics

The byte/encoding bridge with String. Both directions live on Data (see Overview); the exported bytes never include a trailing NUL.

static Data* withString(String* s)

The string’s bytes as a Data, without the trailing NUL (Foundation’s dataUsingEncoding: UTF-8 form). A null s yields an empty Data.

String* stringValue(void)

These bytes as a String. An embedded NUL is copied like any other byte, so the length is preserved, but cString() on the result stops at that NUL (a limit of the C representation).

(0.4)

static Data* withStringEncoded(String* s, StrEncoding enc)

Encodes a String as enc bytes: ENC_UTF8 / ENC_ASCII / ENC_LATIN1 / ENC_UTF16LE / ENC_UTF16BE. A code point the target encoding cannot express (Latin-1 above U+00FF, ASCII above U+007F) becomes ?. UTF-16 emits surrogate pairs above the BMP. ENC_UTF8 is a plain byte copy including any invalid bytes; call s.sanitizedUtf8() first if you want repair. This is the encoding counterpart to String.withEncodedBytes; see String § other encodings.

↑ Topics

String* hexString(void)

The bytes as lowercase hex with no separators, for example "deadbeef". Useful for writing a blob to a log line or comparing it in a test.

↑ Topics

The Object / Comparable / Hashable / Copying hooks.

String* description(void)

The %@ hook: <Data 4: deadbeef> (the length, then hexString).

bool equals(Data* other)
bool equals(Object* other)

Byte-exact equality (equal length and equal bytes). The Object* overload is the protocol slot heterogeneous containers use; it returns false against a non-Data.

i8 compare(Data* other)
i8 compare(Object* other)

Total ordering: lexicographic by unsigned byte, then by length (memcmp order), so a shorter buffer that is a prefix of a longer one sorts first. Returns negative / zero / positive. The Object* overload returns 0 against a non-Data.

u32 hash(void)

FNV-1a over the bytes. This is the Hashable method, so a Data can key a Map or Set. Equal byte sequences always hash the same.

Data* copy(void)

An independent duplicate over the same bytes (the Copying method). Data owns its buffer, so this copies it rather than sharing.

void dealloc(void)

Frees the backing buffer. ARC calls it when the last reference goes away; you do not call it directly.

↑ Topics

#import "Stdio.xc"
#import "Foundation.xc"
i32 main(void)
{
u8 raw[4] = { $DE, $AD, $BE, $EF };
Data* d = Data.withBytes(&raw[0], (u32)4);
Stdio.printf("%s\n", d.hexString().cString()); // deadbeef
Stdio.printf("%s\n", d.description().cString()); // <Data 4: deadbeef>
d.appendByte((u8)$FF);
Stdio.printf("%d\n", (i16)d.length()); // 5
return 0;
}