Add the dependency
In your project’s ziran.toml; pin ref to a commit for reproducible builds.
[dependencies.image_codec]
git = "https://github.com/ziranlang/image-codec.git"
ref = "master"Overview
Consumers import image_codec/api when the dependency is named image_codec.
Exports also expose individual ordinary modules. See API,
fixtures and benchmarks.
From this canonical checkout, with Ziran at ../../ziran:
build-queue -- sh tests/contracts.sh
build-queue -- sh tests/corpus.sh --verify
build-queue -- sh tests/benchmark_contract.sh
build-queue -- sh tests/package_import.sh
ZIRAN_ROOT and ZIRAN select an existing toolchain. Tests use no display,
network, fixture downloads or native codecs. Package import uses local
overrides; a toolchain that requires committed HEADs rejects an unborn package.
Direct module tests exercise the same source without a milestone commit.
Generated outputs stay under ignored build/.
The rebuilt toolchain must provide VecResize and VecResizeBounded; a version string alone does
not identify this development capability before upstream integration.
image-codec/src/api.zi
api
Import with #import "image_codec/api".
StrictColor :: 0Only RGBA8 output and strict color are defined here. Codec engines must explicitly reject unsupported source features rather than ignore them.
FastColor :: 1DecodeOptions :: struct { limits: DecodeLimits; cancellation: *Cancellation; workers: *WorkerBudget; color_mode: s32; }DefaultOptions :: () -> DecodeOptionsValidateOptions :: (options: *DecodeOptions) -> DecodeErrorDecodeProcedure :: #type (input: BorrowedBytes, metadata: ImageMetadata, options: *DecodeOptions) -> DecodeResultUseful reference provider: already decoded, tightly packed RGBA8 bytes.
DecodeRGBA8 :: (input: BorrowedBytes, metadata: ImageMetadata, options: *DecodeOptions) -> DecodeResultUseful reference provider: already decoded, tightly packed RGBA8 bytes. Metadata is supplied by the caller, not guessed from an unframed file.
image-codec/src/errors.zi
errors
Zero is success. Error offsets are input byte offsets, or -1 if unknown.
Import with #import "image_codec/errors".
DecodeError :: struct { code: s32; offset: s64; }DecodeOK :: 0InvalidLimits :: 1InvalidInput :: 2TruncatedInput :: 3MalformedInput :: 4UnsupportedCapability :: 5LimitExceeded :: 6OutputTooSmall :: 7DecodeCancelled :: 8AllocationFailed :: 9WorkerBudgetExceeded :: 10InputOutputFailed :: 11CorrectnessMismatch :: 12Error :: (code: s32, offset: s64) -> DecodeErrorErrorText :: (code: s32) -> stringimage-codec/src/limits.zi
limits
Import with #import "image_codec/limits".
DecodeLimits :: struct { max_input_bytes: s64; max_width: s32; max_height: s32; max_pixels: s64; max_output_bytes: s64; max_working_bytes: s64; max_metadata_bytes: s64; max_frames: s32; max_decoded_frames: s32; max_workers: s32; }DefaultLimits :: () -> DecodeLimitsValidateLimits :: (limits: DecodeLimits) -> DecodeErrorValidateDecodedFrames :: (count: s32, limits: DecodeLimits) -> DecodeErrorRequiredSpan :: (width: s32, height: s32, sample_bytes: s32, stride: s64) -> (s64, DecodeError)Division checks precede multiplication, including hostile strides.
ValidatePixelDimensions :: (width: s32, height: s32, limits: DecodeLimits) -> DecodeErrorStorage-neutral: planar/YUV engines apply their own output byte budget. Positive s32 sides widen to s64, so the pixel product cannot overflow.
ValidateDimensions :: (width: s32, height: s32, limits: DecodeLimits) -> DecodeErrorTight RGBA8 output: four bytes per pixel must fit max_output_bytes.
ValidateInputSize :: (count: s64, limits: DecodeLimits) -> DecodeErrorimage-codec/src/metadata.zi
metadata
Import with #import "image_codec/metadata".
ImageMetadata :: struct { width: s32; height: s32; source_bit_depth: s32; has_alpha: bool; source_premultiplied: bool; frame_count: s32; color_known: bool; color_primaries: s32; transfer_characteristics: s32; matrix_coefficients: s32; full_range: bool; metadata_bytes: s64; }Output dimensions after transforms. Zero source values mean unknown. Image output is always packed straight-alpha RGBA8 in top-to-bottom rows.
ValidateMetadata :: (metadata: ImageMetadata, limits: DecodeLimits) -> DecodeErrorimage-codec/src/bytes.zi
bytes
Import with #import "image_codec/bytes".
BorrowedBytes :: struct { data: []u8; }A synchronous borrow, never an owner. The caller keeps its backing storage alive and unchanged throughout use. Ziran []u8 is not a const slice.
BorrowBytes :: (data: []u8) -> BorrowedBytesByteRange :: (input: BorrowedBytes, offset: s64, count: s64) -> (BorrowedBytes, DecodeError)image-codec/src/buffer.zi
buffer
Import with #import "image_codec/buffer".
Plane :: struct { width: s32; height: s32; stride: s64; sample_bytes: s32; storage: Vec(u8); }One component plane. Samples are 1 or 2 bytes; multi-byte samples use little endian storage. The owning Vec may include row padding.
PlaneView :: struct { width: s32; height: s32; stride: s64; sample_bytes: s32; data: []u8; }OutputBuffer :: struct { data: []u8; stride: s64; }ValidatePlane :: (plane: PlaneView, limits: DecodeLimits) -> DecodeErrorValidateOutput :: (output: OutputBuffer, width: s32, height: s32, limits: DecodeLimits) -> DecodeErrorimage-codec/src/image.zi
image
Import with #import "image_codec/image".
DecodeResult :: struct { is_ok: bool; image: Image; metadata: ImageMetadata; error: DecodeError; }DecodeFailure :: (error: DecodeError) -> DecodeResultAllocateImage :: (metadata: ImageMetadata, limits: DecodeLimits, token: *Cancellation) -> DecodeResultOne fallible bulk allocation. Check cancellation around allocation; pixel providers and row copies add their own checkpoints during actual work.
CopyImageToOutput :: (image: *Image, output: OutputBuffer, limits: DecodeLimits, token: *Cancellation) -> DecodeErrorCaller-owned output borrows its storage. Validation and pre-cancellation precede writes; cancellation during work can leave complete rows written.
image-codec/src/scheduler.zi
scheduler
Import with #import "image_codec/scheduler".
Cancellation :: struct { requested: bool; }Cooperative, single-thread state. Threaded engines must serialize access or adapt their own atomic cancellation to these checkpoints.
Cancel :: (token: *Cancellation)CancellationError :: (token: *Cancellation) -> DecodeErrorWorkerBudget :: struct { max_workers: s32; active_workers: s32; peak_workers: s32; max_working_bytes: s64; working_bytes: s64; peak_working_bytes: s64; }Counts all execution lanes, including the caller. No threads are launched. Engine reservations cover scratch only, separate from input/output bytes.
NewWorkerBudget :: (limits: DecodeLimits) -> WorkerBudgetValidateWorkerBudget :: (budget: *WorkerBudget, limits: DecodeLimits) -> DecodeErrorAcquireWorker :: (budget: *WorkerBudget, token: *Cancellation) -> DecodeErrorReleaseWorker :: (budget: *WorkerBudget) -> DecodeErrorReserveWorkingBytes :: (budget: *WorkerBudget, bytes: s64, token: *Cancellation) -> DecodeErrorReleaseWorkingBytes :: (budget: *WorkerBudget, bytes: s64) -> DecodeErrorimage-codec/src/benchmark.zi
benchmark
Import with #import "image_codec/benchmark".
MaxBenchmarkIterations :: 1024TimingSummary :: struct { p50_ns: s64; p95_ns: s64; total_ns: s64; iterations: s32; error: DecodeError; }SummarizeTimings :: (samples: []s64) -> TimingSummaryNearest-rank percentiles. Sorts caller-owned sample storage in place.
FileBytes :: struct { data: Vec(u8); error: DecodeError; }FileFailure :: (error: DecodeError) -> FileBytesReadBoundedFile :: (path: string, maximum: s64, token: *Cancellation) -> FileBytesSamePixels :: (picture: *Image, expected: []u8) -> boolImageHash :: (picture: *Image) -> u64HashText :: (hash: u64) -> stringBenchmarkResult :: struct { is_ok: bool; timing: TimingSummary; samples_ns: [1024]s64; output_hash: u64; input_bytes: s64; output_bytes: s64; pixel_count: s64; active_workers: s32; error: DecodeError; }BenchmarkFile :: (path: string, expected: []u8, metadata: ImageMetadata, options: *DecodeOptions, provider: DecodeProcedure, iterations: s32) -> BenchmarkResultEach sample opens/reads/closes input then calls the provider. Allocation of input/output is timed. Output validation, hash and destruction are untimed. An initial correctness decode MUST pass before the first timed sample.
BenchmarkJSON :: (result: *BenchmarkResult, provider_name: string, toolchain_version: string) -> stringJSON null represents unavailable measurement, never an invented zero.