LCOV - code coverage report
Current view: top level - src - zstd_native.dart (source / functions) Coverage Total Hit
Test: lcov.info Lines: 90.5 % 42 38
Test Date: 2026-06-16 06:25:02 Functions: - 0 0

            Line data    Source code
       1              : // Copyright 2026 The Authors
       2              : //
       3              : // Licensed under the Apache License, Version 2.0 (the "License");
       4              : // you may not use this file except in compliance with the License.
       5              : // You may obtain a copy of the License at
       6              : //
       7              : //     http://www.apache.org/licenses/LICENSE-2.0
       8              : //
       9              : // Unless required by applicable law or agreed to in writing, software
      10              : // distributed under the License is distributed on an "AS IS" BASIS,
      11              : // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      12              : // See the License for the specific language governing permissions and
      13              : // limitations under the License.
      14              : 
      15              : import 'dart:ffi';
      16              : import 'dart:typed_data';
      17              : 
      18              : import 'package:ffi/ffi.dart';
      19              : 
      20              : import 'third_party/zstd.dart';
      21              : import 'zstd_exception.dart';
      22              : 
      23              : /// Default compression level for Zstd.
      24              : const int defaultLevel = ZSTD_CLEVEL_DEFAULT;
      25              : 
      26              : /// Version of the Zstd library being used.
      27              : const String zStdVersion = ZSTD_VERSION_STRING;
      28              : 
      29              : /// Returns the minimum compression level supported by the Zstd library.
      30              : @Native<Int32 Function()>(symbol: 'ZSTD_minCLevel')
      31              : external int minCLevel();
      32              : 
      33              : /// Returns the maximum compression level supported by the Zstd library.
      34              : @Native<Int32 Function()>(symbol: 'ZSTD_maxCLevel')
      35              : external int maxCLevel();
      36              : 
      37              : @Native<Size Function(Size)>(symbol: 'ZSTD_compressBound')
      38              : external int _compressBound(int srcSize);
      39              : 
      40              : @Native<Size Function(Pointer<Void>, Size, Pointer<Void>, Size, Int32)>(
      41              :   symbol: 'ZSTD_compress',
      42              : )
      43              : external int _compress(
      44              :   Pointer<Void> dst,
      45              :   int dstCapacity,
      46              :   Pointer<Void> src,
      47              :   int srcSize,
      48              :   int compressionLevel,
      49              : );
      50              : 
      51              : @Native<Size Function(Pointer<Void>, Size, Pointer<Void>, Size)>(
      52              :   symbol: 'ZSTD_decompress',
      53              : )
      54              : external int _decompress(
      55              :   Pointer<Void> dst,
      56              :   int dstCapacity,
      57              :   Pointer<Void> src,
      58              :   int compressedSize,
      59              : );
      60              : 
      61              : @Native<Uint64 Function(Pointer<Void>, Size)>(
      62              :   symbol: 'ZSTD_getFrameContentSize',
      63              : )
      64              : external int _getFrameContentSize(Pointer<Void> src, int srcSize);
      65              : 
      66              : @Native<Uint32 Function(Size)>(symbol: 'ZSTD_isError')
      67              : external int _isError(int result);
      68              : 
      69              : @Native<Pointer<Utf8> Function(Size)>(symbol: 'ZSTD_getErrorName')
      70              : external Pointer<Utf8> _getErrorName(int result);
      71              : 
      72              : /// A simple interface for Zstd compression and decompression.
      73              : ///
      74              : /// Use this class for synchronous compression and decompression of byte arrays.
      75              : class ZstdSimple {
      76              :   /// The compression level to use (default: [defaultLevel]).
      77              :   final int level;
      78              : 
      79              :   /// No-op on native platforms; exists so callers can always await
      80              :   /// [ZstdSimple.init] without platform guards.
      81            2 :   static Future<void> init({String? wasmUrl}) async {}
      82              : 
      83              :   /// Creates a new [ZstdSimple] instance with the given [level].
      84              :   ///
      85              :   /// Throws [ArgumentError] if the [level] is invalid.
      86            2 :   ZstdSimple({this.level = defaultLevel}) {
      87            4 :     if (!_isValidCLevel(level)) {
      88            2 :       throw ArgumentError.value(level, 'level', 'Invalid compression level');
      89              :     }
      90              :   }
      91              : 
      92              :   /// Returns the Zstd version string.
      93            2 :   String get version => zStdVersion.toString();
      94              : 
      95            2 :   bool _isValidCLevel(int level) =>
      96            8 :       level >= minCLevel() && level <= maxCLevel();
      97              : 
      98              :   /// Compresses the given [data].
      99              :   ///
     100              :   /// Returns the compressed data as a [Uint8List].
     101              :   /// Throws an [Exception] if an error occurs during compression.
     102            2 :   Uint8List compress(List<int> data) {
     103            2 :     final srcSize = data.length;
     104            2 :     final dstCapacity = _compressBound(srcSize);
     105              : 
     106            4 :     if (_isError(dstCapacity) != 0) {
     107            0 :       final errorName = _getErrorName(dstCapacity).toDartString();
     108            0 :       throw ZstdException('compressBound error: $errorName');
     109              :     }
     110              : 
     111              :     final srcPtr = malloc<Uint8>(srcSize);
     112              :     final dstPtr = malloc<Uint8>(dstCapacity);
     113              : 
     114              :     try {
     115            4 :       srcPtr.asTypedList(srcSize).setAll(0, data);
     116              : 
     117            2 :       final compressedSize = _compress(
     118            2 :         dstPtr.cast(),
     119              :         dstCapacity,
     120            2 :         srcPtr.cast(),
     121              :         srcSize,
     122            2 :         level,
     123              :       );
     124              : 
     125            4 :       if (_isError(compressedSize) != 0) {
     126            0 :         final errorName = _getErrorName(compressedSize).toDartString();
     127            0 :         throw ZstdException('compression error: $errorName');
     128              :       }
     129              : 
     130            4 :       final result = Uint8List.fromList(dstPtr.asTypedList(compressedSize));
     131              :       return result;
     132              :     } finally {
     133            2 :       malloc.free(srcPtr);
     134            2 :       malloc.free(dstPtr);
     135              :     }
     136              :   }
     137              : 
     138              :   /// Decompresses the given [data].
     139              :   ///
     140              :   /// Returns the decompressed data as a [Uint8List].
     141              :   /// Throws an [Exception] if an error occurs during decompression.
     142            2 :   Uint8List decompress(List<int> data) {
     143            2 :     final compressedSize = data.length;
     144              :     final srcPtr = malloc<Uint8>(compressedSize);
     145              :     try {
     146            4 :       srcPtr.asTypedList(compressedSize).setAll(0, data);
     147            2 :       final decompressedSize = _getFrameContentSize(
     148            2 :         srcPtr.cast(),
     149              :         compressedSize,
     150              :       );
     151              : 
     152            4 :       if (decompressedSize == -1) {
     153            1 :         throw ZstdException(
     154              :           'decompression error: unknown content size. Use streaming API.',
     155              :         );
     156              :       }
     157            4 :       if (decompressedSize == -2) {
     158            1 :         throw ZstdException('decompression error: invalid frame header.');
     159              :       }
     160              : 
     161              :       final dstPtr = malloc<Uint8>(decompressedSize);
     162              :       try {
     163            2 :         final resultSize = _decompress(
     164            2 :           dstPtr.cast(),
     165              :           decompressedSize,
     166            2 :           srcPtr.cast(),
     167              :           compressedSize,
     168              :         );
     169              : 
     170            4 :         if (_isError(resultSize) != 0) {
     171            2 :           final errorName = _getErrorName(resultSize).toDartString();
     172            2 :           throw ZstdException('decompression error: $errorName');
     173              :         }
     174              : 
     175            4 :         final result = Uint8List.fromList(dstPtr.asTypedList(resultSize));
     176              :         return result;
     177              :       } finally {
     178            2 :         malloc.free(dstPtr);
     179              :       }
     180              :     } finally {
     181            2 :       malloc.free(srcPtr);
     182              :     }
     183              :   }
     184              : }
        

Generated by: LCOV version 2.0-1