diff --git a/.gitignore b/.gitignore index 4ca4e6d..afe68c8 100644 --- a/.gitignore +++ b/.gitignore @@ -69,4 +69,7 @@ env/ venv/ ENV/ env.bak/ -venv.bak/ \ No newline at end of file +venv.bak/ + +# Test/example output +*.zarr \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b2fd0c..ca32fe9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,6 +21,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) option(BUILD_PYTHON "Build Python bindings" OFF) +option(BUILD_EXAMPLES "Build examples" OFF) option(BUILD_BENCHMARK "Build benchmarks" OFF) if(CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) @@ -34,6 +35,12 @@ else () message(STATUS "Skipping test targets") endif () +if (${BUILD_EXAMPLES}) + add_subdirectory(examples) +else () + message(STATUS "Skipping examples") +endif () + if (BUILD_BENCHMARK) add_subdirectory(benchmarks) else () diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..545e084 --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,35 @@ +set(project acquire-zarr) + +set(examples + zarrv2-raw-filesystem + zarrv2-compressed-s3 + zarrv3-compressed-filesystem + zarrv3-raw-multiscale-filesystem + zarrv3-raw-s3 +) + +foreach (name ${examples}) + set(tgt "${project}-${name}") + add_executable(${tgt} ${name}.c) + set_target_properties(${tgt} PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + ) + target_include_directories(${tgt} PRIVATE + ${PROJECT_SOURCE_DIR}/include + ) + target_link_libraries(${tgt} PRIVATE + acquire-logger + acquire-zarr + nlohmann_json::nlohmann_json + miniocpp::miniocpp + ) + + add_test(NAME example-${tgt} COMMAND ${tgt}) + + set(test_labels "examples") + if (name MATCHES ".*s3.*") + list(APPEND test_labels "s3") + endif () + + set_tests_properties(example-${tgt} PROPERTIES LABELS "${test_labels}") +endforeach () \ No newline at end of file diff --git a/examples/python/zarrv2-compressed-multiscale-filesystem.py b/examples/python/zarrv2-compressed-multiscale-filesystem.py new file mode 100644 index 0000000..c81b698 --- /dev/null +++ b/examples/python/zarrv2-compressed-multiscale-filesystem.py @@ -0,0 +1,68 @@ +# Basic Zarr V2 to filesystem +import numpy as np +from acquire_zarr import ( + StreamSettings, ZarrStream, Dimension, DimensionType, ZarrVersion, DataType, Compressor, CompressionCodec, + CompressionSettings +) + + +def make_sample_data(): + return np.random.randint( + 0, 65535, + (32, 48, 64), # Shape matches chunk size for time dimension + dtype=np.int32 + ) + + +def main(): + # Configure stream settings + settings = StreamSettings() + + # Configure compression + settings.compression = CompressionSettings( + compressor=Compressor.BLOSC1, + codec=CompressionCodec.BLOSC_LZ4, + level=1, + shuffle=2, # bitshuffle + ) + + # Configure dimensions (t, y, x) + settings.dimensions.extend([ + Dimension( + name="t", + kind=DimensionType.TIME, + array_size_px=0, # Unlimited + chunk_size_px=32, + shard_size_chunks=1, + ), + Dimension( + name="y", + kind=DimensionType.SPACE, + array_size_px=48, + chunk_size_px=16, + shard_size_chunks=1, + ), + Dimension( + name="x", + kind=DimensionType.SPACE, + array_size_px=64, + chunk_size_px=32, + shard_size_chunks=1, + ), + ]) + + settings.store_path = "output_v2_multiscale.zarr" + settings.version = ZarrVersion.V2 + settings.data_type = DataType.INT32 + settings.multiscale = True + + # Create stream + stream = ZarrStream(settings) + + # Create and write sample data + for i in range(10): + stream.append(make_sample_data()) + + +if __name__ == "__main__": + main() diff --git a/examples/python/zarrv2-compressed-s3.py b/examples/python/zarrv2-compressed-s3.py new file mode 100644 index 0000000..edc0263 --- /dev/null +++ b/examples/python/zarrv2-compressed-s3.py @@ -0,0 +1,80 @@ +# Zarr V2 with ZSTD compression to S3 +import numpy as np +from acquire_zarr import ( + StreamSettings, ZarrStream, Dimension, DimensionType, ZarrVersion, + DataType, Compressor, CompressionCodec, CompressionSettings, S3Settings +) + + +def make_sample_data(): + return np.random.randint( + 0, 65535, + (32, 3, 48, 64), # Shape matches chunk sizes + dtype=np.int32 + ) + +def main(): + settings = StreamSettings() + + # Configure S3 + settings.s3 = S3Settings( + endpoint="http://localhost:9000", + bucket_name="mybucket", + access_key_id="myaccesskey", + secret_access_key="mysecretkey" + ) + + # Configure compression + settings.compression = CompressionSettings( + compressor=Compressor.BLOSC1, + codec=CompressionCodec.BLOSC_ZSTD, + level=1, + shuffle=1, + ) + + # Configure 4D array (t, c, y, x) + settings.dimensions.extend([ + Dimension( + name="t", + kind=DimensionType.TIME, + array_size_px=0, # Unlimited + chunk_size_px=32, + shard_size_chunks=1, + ), + Dimension( + name="c", + kind=DimensionType.CHANNEL, + array_size_px=3, + chunk_size_px=3, + shard_size_chunks=1, + ), + Dimension( + name="y", + kind=DimensionType.SPACE, + array_size_px=48, + chunk_size_px=16, + shard_size_chunks=1, + ), + Dimension( + name="x", + kind=DimensionType.SPACE, + array_size_px=64, + chunk_size_px=32, + shard_size_chunks=1, + ), + ]) + + settings.store_path = "output_v2_s3.zarr" + settings.version = ZarrVersion.V2 + settings.data_type = DataType.INT32 + + # Create stream + stream = ZarrStream(settings) + + # Create and write sample data + for i in range(10): + stream.append(make_sample_data()) + + +if __name__ == "__main__": + main() diff --git a/examples/python/zarrv2-raw-filesystem.py b/examples/python/zarrv2-raw-filesystem.py new file mode 100644 index 0000000..fea7528 --- /dev/null +++ b/examples/python/zarrv2-raw-filesystem.py @@ -0,0 +1,57 @@ +# Basic Zarr V2 to filesystem +import numpy as np +from acquire_zarr import ( + StreamSettings, ZarrStream, Dimension, DimensionType, ZarrVersion, DataType +) + + +def make_sample_data(): + return np.random.randint( + 0, 65535, + (32, 48, 64), # Shape matches chunk size for time dimension + dtype=np.int32 + ) + +def main(): + # Configure stream settings + settings = StreamSettings() + + # Configure dimensions (t, y, x) + settings.dimensions.extend([ + Dimension( + name="t", + kind=DimensionType.TIME, + array_size_px=0, # Unlimited + chunk_size_px=32, + shard_size_chunks=1, + ), + Dimension( + name="y", + kind=DimensionType.SPACE, + array_size_px=48, + chunk_size_px=16, + shard_size_chunks=1, + ), + Dimension( + name="x", + kind=DimensionType.SPACE, + array_size_px=64, + chunk_size_px=32, + shard_size_chunks=1, + ), + ]) + + settings.store_path = "output_v2.zarr" + settings.version = ZarrVersion.V2 + settings.data_type = DataType.INT32 + + # Create stream + stream = ZarrStream(settings) + + # Create and write sample data + for i in range(10): + stream.append(make_sample_data()) + + +if __name__ == "__main__": + main() diff --git a/examples/python/zarrv3-compressed-filesystem.py b/examples/python/zarrv3-compressed-filesystem.py new file mode 100644 index 0000000..54cf6c2 --- /dev/null +++ b/examples/python/zarrv3-compressed-filesystem.py @@ -0,0 +1,78 @@ +# Zarr V3 with LZ4 compression to filesystem +import numpy as np +from acquire_zarr import ( + StreamSettings, ZarrStream, Dimension, DimensionType, ZarrVersion, + DataType, Compressor, CompressionCodec, CompressionSettings +) + + +def make_sample_data(): + return np.random.randint( + 0, 65535, + (5, 4, 2, 48, 64), # Shape matches chunk sizes + dtype=np.uint16 + ) + +def main(): + settings = StreamSettings() + + # Configure compression + settings.compression = CompressionSettings( + compressor=Compressor.BLOSC1, + codec=CompressionCodec.BLOSC_LZ4, + level=1, + shuffle=1, + ) + + # Configure 5D array (t, c, z, y, x) + settings.dimensions.extend([ + Dimension( + name="t", + kind=DimensionType.TIME, + array_size_px=10, + chunk_size_px=5, + shard_size_chunks=2, + ), + Dimension( + name="c", + kind=DimensionType.CHANNEL, + array_size_px=8, + chunk_size_px=4, + shard_size_chunks=2, + ), + Dimension( + name="z", + kind=DimensionType.SPACE, + array_size_px=6, + chunk_size_px=2, + shard_size_chunks=1, + ), + Dimension( + name="y", + kind=DimensionType.SPACE, + array_size_px=48, + chunk_size_px=16, + shard_size_chunks=1, + ), + Dimension( + name="x", + kind=DimensionType.SPACE, + array_size_px=64, + chunk_size_px=16, + shard_size_chunks=2, + ), + ]) + + settings.store_path = "output_v3_compressed.zarr" + settings.version = ZarrVersion.V3 + settings.data_type = DataType.UINT16 + + # Create stream + stream = ZarrStream(settings) + + # Write sample data + stream.append(make_sample_data()) + + +if __name__ == "__main__": + main() diff --git a/examples/python/zarrv3-raw-s3.py b/examples/python/zarrv3-raw-s3.py new file mode 100644 index 0000000..34eabd6 --- /dev/null +++ b/examples/python/zarrv3-raw-s3.py @@ -0,0 +1,71 @@ +# zarr_v3_s3_raw.py +import numpy as np +from acquire_zarr import ( + StreamSettings, ZarrStream, Dimension, DimensionType, ZarrVersion, + DataType, S3Settings +) + + +def make_sample_data(): + return np.random.randint( + 0, 65535, + (5, 2, 48, 64), # Shape matches chunk sizes + dtype=np.uint16 + ) + +def main(): + settings = StreamSettings() + + # Configure S3 + settings.s3 = S3Settings( + endpoint="http://localhost:9000", + bucket_name="mybucket", + access_key_id="myaccesskey", + secret_access_key="mysecretkey" + ) + + # Configure 4D array (t, z, y, x) + settings.dimensions.extend([ + Dimension( + name="t", + kind=DimensionType.TIME, + array_size_px=0, # Unlimited + chunk_size_px=5, + shard_size_chunks=2, + ), + Dimension( + name="z", + kind=DimensionType.SPACE, + array_size_px=10, + chunk_size_px=2, + shard_size_chunks=1, + ), + Dimension( + name="y", + kind=DimensionType.SPACE, + array_size_px=48, + chunk_size_px=16, + shard_size_chunks=1, + ), + Dimension( + name="x", + kind=DimensionType.SPACE, + array_size_px=64, + chunk_size_px=16, + shard_size_chunks=2, + ), + ]) + + settings.store_path = "output_v3_s3.zarr" + settings.version = ZarrVersion.V3 + settings.data_type = DataType.UINT16 + + # Create stream + stream = ZarrStream(settings) + + # Write sample data + stream.append(make_sample_data()) + + +if __name__ == "__main__": + main() diff --git a/examples/zarrv2-compressed-s3.c b/examples/zarrv2-compressed-s3.c new file mode 100644 index 0000000..ef3ef1b --- /dev/null +++ b/examples/zarrv2-compressed-s3.c @@ -0,0 +1,106 @@ +/// @file zarrv2-compressed-s3.c +/// @brief Zarr V2 with ZSTD compression to S3 +#include "acquire.zarr.h" +#include +#include + +int main() { + // Configure S3 + ZarrS3Settings s3 = { + .endpoint = "http://localhost:9000", + .bucket_name = "mybucket", + .access_key_id = "myaccesskey", + .secret_access_key = "mysecretkey" + }; + + // Configure compression + ZarrCompressionSettings compression = { + .compressor = ZarrCompressor_Blosc1, + .codec = ZarrCompressionCodec_BloscZstd, + .level = 1, + .shuffle = 1 + }; + + // Configure stream settings + ZarrStreamSettings settings = { + .store_path = "output_v2_s3.zarr", + .s3_settings = &s3, + .compression_settings = &compression, + .data_type = ZarrDataType_int32, + .version = ZarrVersion_2 + }; + + // Set up dimensions (t, c, y, x) + ZarrStreamSettings_create_dimension_array(&settings, 4); + + settings.dimensions[0] = (ZarrDimensionProperties){ + .name = "t", + .type = ZarrDimensionType_Time, + .array_size_px = 0, // Unlimited + .chunk_size_px = 32, + .shard_size_chunks = 1 + }; + + settings.dimensions[1] = (ZarrDimensionProperties){ + .name = "c", + .type = ZarrDimensionType_Channel, + .array_size_px = 3, + .chunk_size_px = 3, + .shard_size_chunks = 1 + }; + + settings.dimensions[2] = (ZarrDimensionProperties){ + .name = "y", + .type = ZarrDimensionType_Space, + .array_size_px = 48, + .chunk_size_px = 16, + .shard_size_chunks = 1 + }; + + settings.dimensions[3] = (ZarrDimensionProperties){ + .name = "x", + .type = ZarrDimensionType_Space, + .array_size_px = 64, + .chunk_size_px = 32, + .shard_size_chunks = 1 + }; + + // Create stream + ZarrStream* stream = ZarrStream_create(&settings); + if (!stream) { + fprintf(stderr, "Failed to create stream\n"); + return 1; + } + + // Create sample data + const size_t width = 64; + const size_t height = 48; + int32_t* frame = (int32_t*)malloc(width * height * sizeof(int32_t)); + + // Write frames + size_t bytes_written; + for (int i = 0; i < 10; i++) { + // Fill frame with sample data + for (size_t j = 0; j < width * height; j++) { + frame[j] = i * 1000 + j; + } + + ZarrStatusCode status = ZarrStream_append( + stream, + frame, + width * height * sizeof(int32_t), + &bytes_written + ); + + if (status != ZarrStatusCode_Success) { + fprintf(stderr, "Failed to append frame: %s\n", + Zarr_get_status_message(status)); + break; + } + } + + // Cleanup + free(frame); + ZarrStream_destroy(stream); + return 0; +} diff --git a/examples/zarrv2-raw-filesystem.c b/examples/zarrv2-raw-filesystem.c new file mode 100644 index 0000000..99b9949 --- /dev/null +++ b/examples/zarrv2-raw-filesystem.c @@ -0,0 +1,85 @@ +/// @file zarrv2-raw-filesystem.c +/// @brief Basic Zarr V2 streaming to filesystem +#include "acquire.zarr.h" +#include +#include + +int main() { + // Configure stream settings + ZarrStreamSettings settings = { + .store_path = "output_v2.zarr", + .s3_settings = NULL, + .compression_settings = NULL, + .data_type = ZarrDataType_int32, + .version = ZarrVersion_2 + }; + + // Set up dimensions (t, y, x) + ZarrStreamSettings_create_dimension_array(&settings, 3); + + // Time dimension - unlimited size (0) + settings.dimensions[0] = (ZarrDimensionProperties){ + .name = "t", + .type = ZarrDimensionType_Time, + .array_size_px = 0, + .chunk_size_px = 32, + .shard_size_chunks = 1 + }; + + // Y dimension - 48 pixels + settings.dimensions[1] = (ZarrDimensionProperties){ + .name = "y", + .type = ZarrDimensionType_Space, + .array_size_px = 48, + .chunk_size_px = 16, + .shard_size_chunks = 1 + }; + + // X dimension - 64 pixels + settings.dimensions[2] = (ZarrDimensionProperties){ + .name = "x", + .type = ZarrDimensionType_Space, + .array_size_px = 64, + .chunk_size_px = 32, + .shard_size_chunks = 1 + }; + + // Create stream + ZarrStream* stream = ZarrStream_create(&settings); + if (!stream) { + fprintf(stderr, "Failed to create stream\n"); + return 1; + } + + // Create sample data + const size_t width = 64; + const size_t height = 48; + int32_t* frame = (int32_t*)malloc(width * height * sizeof(int32_t)); + + // Write some frames + size_t bytes_written; + for (int i = 0; i < 10; i++) { + // Fill frame with sample data + for (size_t j = 0; j < width * height; j++) { + frame[j] = i * 1000 + j; + } + + ZarrStatusCode status = ZarrStream_append( + stream, + frame, + width * height * sizeof(int32_t), + &bytes_written + ); + + if (status != ZarrStatusCode_Success) { + fprintf(stderr, "Failed to append frame: %s\n", + Zarr_get_status_message(status)); + break; + } + } + + // Cleanup + free(frame); + ZarrStream_destroy(stream); + return 0; +} \ No newline at end of file diff --git a/examples/zarrv3-compressed-filesystem.c b/examples/zarrv3-compressed-filesystem.c new file mode 100644 index 0000000..bf6a911 --- /dev/null +++ b/examples/zarrv3-compressed-filesystem.c @@ -0,0 +1,106 @@ +/// @file zarr-v3-compressed-filesystem.c +/// @brief Zarr V3 with LZ4 compression to filesystem +#include "acquire.zarr.h" +#include +#include + +int main() { + // Configure compression + ZarrCompressionSettings compression = { + .compressor = ZarrCompressor_Blosc1, + .codec = ZarrCompressionCodec_BloscLZ4, + .level = 1, + .shuffle = 1 + }; + + // Configure stream settings + ZarrStreamSettings settings = { + .store_path = "output_v3_compressed.zarr", + .s3_settings = NULL, + .compression_settings = &compression, + .data_type = ZarrDataType_uint16, + .version = ZarrVersion_3 + }; + + // Set up 5D array (t, c, z, y, x) + ZarrStreamSettings_create_dimension_array(&settings, 5); + + settings.dimensions[0] = (ZarrDimensionProperties){ + .name = "t", + .type = ZarrDimensionType_Time, + .array_size_px = 10, + .chunk_size_px = 5, + .shard_size_chunks = 2 + }; + + settings.dimensions[1] = (ZarrDimensionProperties){ + .name = "c", + .type = ZarrDimensionType_Channel, + .array_size_px = 8, + .chunk_size_px = 4, + .shard_size_chunks = 2 + }; + + settings.dimensions[2] = (ZarrDimensionProperties){ + .name = "z", + .type = ZarrDimensionType_Space, + .array_size_px = 6, + .chunk_size_px = 2, + .shard_size_chunks = 1 + }; + + settings.dimensions[3] = (ZarrDimensionProperties){ + .name = "y", + .type = ZarrDimensionType_Space, + .array_size_px = 48, + .chunk_size_px = 16, + .shard_size_chunks = 1 + }; + + settings.dimensions[4] = (ZarrDimensionProperties){ + .name = "x", + .type = ZarrDimensionType_Space, + .array_size_px = 64, + .chunk_size_px = 16, + .shard_size_chunks = 2 + }; + + // Create stream + ZarrStream* stream = ZarrStream_create(&settings); + if (!stream) { + fprintf(stderr, "Failed to create stream\n"); + return 1; + } + + // Create sample data + const size_t width = 64; + const size_t height = 48; + uint16_t* frame = (uint16_t*)malloc(width * height * sizeof(uint16_t)); + + // Write frames + size_t bytes_written; + for (int i = 0; i < 10; i++) { + // Fill frame with sample data + for (size_t j = 0; j < width * height; j++) { + frame[j] = i * 1000 + j; + } + + ZarrStatusCode status = ZarrStream_append( + stream, + frame, + width * height * sizeof(uint16_t), + &bytes_written + ); + + if (status != ZarrStatusCode_Success) { + fprintf(stderr, "Failed to append frame: %s\n", + Zarr_get_status_message(status)); + break; + } + } + + // Cleanup + free(frame); + ZarrStream_destroy(stream); + return 0; +} \ No newline at end of file diff --git a/examples/zarrv3-raw-multiscale-filesystem.c b/examples/zarrv3-raw-multiscale-filesystem.c new file mode 100644 index 0000000..082713d --- /dev/null +++ b/examples/zarrv3-raw-multiscale-filesystem.c @@ -0,0 +1,99 @@ +/// @file zarr-v3-compressed-filesystem.c +/// @brief Zarr V3 with LZ4 compression to filesystem +#include "acquire.zarr.h" +#include +#include +#include + +int +main() +{ + // Configure stream settings + ZarrStreamSettings settings = { + .store_path = "output_v3_multiscale.zarr", + .s3_settings = NULL, + .compression_settings = NULL, + .multiscale = true, + .data_type = ZarrDataType_uint16, + .version = ZarrVersion_3, + }; + + // Set up 5D array (t, c, z, y, x) + ZarrStreamSettings_create_dimension_array(&settings, 5); + + settings.dimensions[0] = (ZarrDimensionProperties){ + .name = "t", + .type = ZarrDimensionType_Time, + .array_size_px = 10, + .chunk_size_px = 5, + .shard_size_chunks = 2, + }; + + settings.dimensions[1] = (ZarrDimensionProperties){ + .name = "c", + .type = ZarrDimensionType_Channel, + .array_size_px = 8, + .chunk_size_px = 4, + .shard_size_chunks = 2, + }; + + settings.dimensions[2] = (ZarrDimensionProperties){ + .name = "z", + .type = ZarrDimensionType_Space, + .array_size_px = 6, + .chunk_size_px = 2, + .shard_size_chunks = 1, + }; + + settings.dimensions[3] = (ZarrDimensionProperties){ + .name = "y", + .type = ZarrDimensionType_Space, + .array_size_px = 48, + .chunk_size_px = 16, + .shard_size_chunks = 1, + }; + + settings.dimensions[4] = (ZarrDimensionProperties){ + .name = "x", + .type = ZarrDimensionType_Space, + .array_size_px = 64, + .chunk_size_px = 16, + .shard_size_chunks = 2, + }; + + // Create stream + ZarrStream* stream = ZarrStream_create(&settings); + if (!stream) { + fprintf(stderr, "Failed to create stream\n"); + return 1; + } + + // Create sample data + const size_t width = 64; + const size_t height = 48; + uint16_t* frame = (uint16_t*)malloc(width * height * sizeof(uint16_t)); + + // Write frames + size_t bytes_written; + for (int i = 0; i < 10; i++) { + // Fill frame with sample data + for (size_t j = 0; j < width * height; j++) { + frame[j] = i * 1000 + j; + } + + ZarrStatusCode status = ZarrStream_append( + stream, frame, width * height * sizeof(uint16_t), &bytes_written); + + if (status != ZarrStatusCode_Success) { + fprintf(stderr, + "Failed to append frame: %s\n", + Zarr_get_status_message(status)); + break; + } + } + + // Cleanup + free(frame); + ZarrStream_destroy(stream); + return 0; +} \ No newline at end of file diff --git a/examples/zarrv3-raw-s3.c b/examples/zarrv3-raw-s3.c new file mode 100644 index 0000000..6535d26 --- /dev/null +++ b/examples/zarrv3-raw-s3.c @@ -0,0 +1,98 @@ +/// @file zarrv3-raw-s3.c +/// @brief Zarr V3 with uncompressed data to S3 +#include "acquire.zarr.h" +#include +#include + +int main() { + // Configure S3 + ZarrS3Settings s3 = { + .endpoint = "http://localhost:9000", + .bucket_name = "mybucket", + .access_key_id = "myaccesskey", + .secret_access_key = "mysecretkey" + }; + + // Configure stream settings + ZarrStreamSettings settings = { + .store_path = "output_v3_s3.zarr", + .s3_settings = &s3, + .compression_settings = NULL, // No compression + .data_type = ZarrDataType_uint16, + .version = ZarrVersion_3 + }; + + // Set up dimensions (t, z, y, x) + ZarrStreamSettings_create_dimension_array(&settings, 4); + + settings.dimensions[0] = (ZarrDimensionProperties){ + .name = "t", + .type = ZarrDimensionType_Time, + .array_size_px = 0, // Unlimited + .chunk_size_px = 5, + .shard_size_chunks = 2 + }; + + settings.dimensions[1] = (ZarrDimensionProperties){ + .name = "z", + .type = ZarrDimensionType_Space, + .array_size_px = 10, + .chunk_size_px = 2, + .shard_size_chunks = 1 + }; + + settings.dimensions[2] = (ZarrDimensionProperties){ + .name = "y", + .type = ZarrDimensionType_Space, + .array_size_px = 48, + .chunk_size_px = 16, + .shard_size_chunks = 1 + }; + + settings.dimensions[3] = (ZarrDimensionProperties){ + .name = "x", + .type = ZarrDimensionType_Space, + .array_size_px = 64, + .chunk_size_px = 16, + .shard_size_chunks = 2 + }; + + // Create stream + ZarrStream* stream = ZarrStream_create(&settings); + if (!stream) { + fprintf(stderr, "Failed to create stream\n"); + return 1; + } + + // Create sample data + const size_t width = 64; + const size_t height = 48; + uint16_t* frame = (uint16_t*)malloc(width * height * sizeof(uint16_t)); + + // Write frames + size_t bytes_written; + for (int i = 0; i < 10; i++) { + // Fill frame with sample data + for (size_t j = 0; j < width * height; j++) { + frame[j] = i * 1000 + j; + } + + ZarrStatusCode status = ZarrStream_append( + stream, + frame, + width * height * sizeof(uint16_t), + &bytes_written + ); + + if (status != ZarrStatusCode_Success) { + fprintf(stderr, "Failed to append frame: %s\n", + Zarr_get_status_message(status)); + break; + } + } + + // Cleanup + free(frame); + ZarrStream_destroy(stream); + return 0; +} \ No newline at end of file