mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 03:51:18 +00:00
* stdlib: implement `std::compression::zip` and `std::compression::deflate` - C3 implementation of DEFLATE (RFC 1951) and ZIP archive handling. - Support for reading and writing archives using STORE and DEFLATE methods. - Decompression supports both fixed and dynamic Huffman blocks. - Compression using greedy LZ77 matching. - Zero dependencies on libc. - Stream-based entry reading and writing. - Full unit test coverage. NOTE: This is an initial implementation. Future improvements could be: - Optimization of the LZ77 matching (lazy matching). - Support for dynamic Huffman blocks in compression. - ZIP64 support for large files/archives. - Support for encryption and additional compression methods. * optimizations+refactoring deflate: - replace linear search with hash-based match finding. - implement support for dynamic Huffman blocks using the Package-Merge algorithm. - add streaming decompression. - add buffered StreamBitReader. zip: - add ZIP64 support. - add CP437 and UTF-8 filename encoding detection. - add DOS date/time conversion and timestamp preservation. - add ZipEntryReader for streaming entry reads. - implement ZipArchive.extract and ZipArchive.recover helpers. other: - Add `set_modified_time` to std::io; - Add benchmarks and a few more unit tests. * zip: add archive comment support add tests * forgot to rename the benchmark :( * detect utf8 names on weird zips fix method not passed to open_writer * another edge case where directory doesn't end with / * testing utilities - detect encrypted zip - `ZipArchive.open_writer` default to DEFLATE * fix zip64 creation, add tests * fix ZIP header endianness for big-endian compatibility Update ZipLFH, ZipCDH, ZipEOCD, Zip64EOCD, and Zip64Locator structs to use little-endian bitstruct types from std::core::bitorder * fix ZipEntryReader position tracking and seek logic ZIP_METHOD_STORE added a test to track this * add package-merge algorithm attribution Thanks @konimarti * standalone deflate_benchmark.c3 against `miniz` * fix integer overflows, leaks and improve safety * a few safety for 32-bit systems and tests * deflate compress optimization * improve match finding, hash updates, and buffer usage * use ulong for zip offsets * style changes (#18) * style changes * update tests * style changes in `deflate.c3` * fix typo * Allocator first. Some changes to deflate to use `copy_to` * Fix missing conversion on 32 bits. * Fix deflate stream. Formatting. Prefer switch over if-elseif * - Stream functions now use long/ulong rather than isz/usz for seek/available. - `instream.seek` is replaced by `set_cursor` and `cursor`. - `instream.available`, `cursor` etc are long/ulong rather than isz/usz to be correct on 32-bit. * Update to constdef * Fix test --------- Co-authored-by: Book-reader <thevoid@outlook.co.nz> Co-authored-by: Christoffer Lerno <christoffer@aegik.com>
145 lines
4.0 KiB
Plaintext
145 lines
4.0 KiB
Plaintext
module std::io::os @if(env::LIBC);
|
|
import libc;
|
|
|
|
<*
|
|
@require mode.len > 0
|
|
@require filename.len > 0
|
|
*>
|
|
fn void*? native_fopen(String filename, String mode) @inline => @stack_mem(256; Allocator smem)
|
|
{
|
|
$if env::WIN32:
|
|
void* file = libc::_wfopen(filename.to_wstring(smem), mode.to_wstring(smem))!;
|
|
$else
|
|
void* file = libc::fopen(filename.zstr_copy(smem), mode.zstr_copy(smem));
|
|
$endif
|
|
return file ?: file_open_errno()~;
|
|
}
|
|
|
|
fn void? native_remove(String filename) => @stack_mem(256; Allocator smem)
|
|
{
|
|
$if env::WIN32:
|
|
CInt result = libc::_wremove(filename.to_wstring(smem))!;
|
|
$else
|
|
CInt result = libc::remove(filename.zstr_copy(smem));
|
|
$endif
|
|
if (result)
|
|
{
|
|
switch (libc::errno())
|
|
{
|
|
case errno::ENOENT:
|
|
return io::FILE_NOT_FOUND~;
|
|
case errno::EACCES:
|
|
default:
|
|
return io::FILE_CANNOT_DELETE~;
|
|
}
|
|
}
|
|
}
|
|
|
|
<*
|
|
@require mode.len > 0
|
|
@require filename.len > 0
|
|
*>
|
|
fn void*? native_freopen(void* file, String filename, String mode) @inline => @stack_mem(256; Allocator smem)
|
|
{
|
|
$if env::WIN32:
|
|
file = libc::_wfreopen(filename.to_wstring(smem), mode.to_wstring(smem), file)!;
|
|
$else
|
|
file = libc::freopen(filename.zstr_copy(smem), mode.zstr_copy(smem), file);
|
|
$endif
|
|
return file ?: file_open_errno()~;
|
|
}
|
|
|
|
fn void? native_fseek(void* file, long offset, SeekOrigin seek_mode) @inline
|
|
{
|
|
if (libc::fseek(file, (SeekIndex)offset, seek_mode.ordinal)) return file_seek_errno()~;
|
|
}
|
|
|
|
|
|
fn long? native_ftell(CFile file) @inline
|
|
{
|
|
long index = libc::ftell(file);
|
|
return index >= 0 ? index : file_seek_errno()~;
|
|
}
|
|
|
|
fn usz? native_fwrite(CFile file, char[] buffer) @inline
|
|
{
|
|
return libc::fwrite(buffer.ptr, 1, buffer.len, file);
|
|
}
|
|
|
|
fn void? native_fputc(CInt c, CFile stream) @inline
|
|
{
|
|
if (libc::fputc(c, stream) == libc::EOF) return io::EOF~;
|
|
}
|
|
|
|
fn usz? native_fread(CFile file, char[] buffer) @inline
|
|
{
|
|
return libc::fread(buffer.ptr, 1, buffer.len, file);
|
|
}
|
|
|
|
macro fault file_open_errno() @local
|
|
{
|
|
switch (libc::errno())
|
|
{
|
|
case errno::EACCES: return io::NO_PERMISSION;
|
|
case errno::EDQUOT: return io::OUT_OF_SPACE;
|
|
case errno::EBADF: return io::FILE_NOT_VALID;
|
|
case errno::EEXIST: return io::ALREADY_EXISTS;
|
|
case errno::EINTR: return io::INTERRUPTED;
|
|
case errno::EFAULT: return io::GENERAL_ERROR;
|
|
case errno::EISDIR: return io::FILE_IS_DIR;
|
|
case errno::ELOOP: return io::SYMLINK_FAILED;
|
|
case errno::EMFILE: return io::TOO_MANY_DESCRIPTORS;
|
|
case errno::ENAMETOOLONG: return io::NAME_TOO_LONG;
|
|
case errno::ENFILE: return io::OUT_OF_SPACE;
|
|
case errno::ENOTDIR: return io::FILE_NOT_DIR;
|
|
case errno::ENOENT: return io::FILE_NOT_FOUND;
|
|
case errno::ENOSPC: return io::OUT_OF_SPACE;
|
|
case errno::ENXIO: return io::FILE_NOT_FOUND;
|
|
case errno::EOVERFLOW: return io::OVERFLOW;
|
|
case errno::EROFS: return io::READ_ONLY;
|
|
case errno::EOPNOTSUPP: return io::UNSUPPORTED_OPERATION;
|
|
case errno::EIO: return io::INCOMPLETE_WRITE;
|
|
case errno::EWOULDBLOCK: return io::WOULD_BLOCK;
|
|
default: return io::UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
macro fault file_seek_errno() @local
|
|
{
|
|
switch (libc::errno())
|
|
{
|
|
case errno::ESPIPE: return io::FILE_IS_PIPE;
|
|
case errno::EPIPE: return io::FILE_IS_PIPE;
|
|
case errno::EOVERFLOW: return io::OVERFLOW;
|
|
case errno::ENXIO: return io::FILE_NOT_FOUND;
|
|
case errno::ENOSPC: return io::OUT_OF_SPACE;
|
|
case errno::EIO: return io::INCOMPLETE_WRITE;
|
|
case errno::EINVAL: return io::INVALID_POSITION;
|
|
case errno::EINTR: return io::INTERRUPTED;
|
|
case errno::EFBIG: return io::OUT_OF_SPACE;
|
|
case errno::EBADF: return io::FILE_NOT_VALID;
|
|
case errno::EAGAIN: return io::WOULD_BLOCK;
|
|
default: return io::UNKNOWN_ERROR;
|
|
}
|
|
}
|
|
|
|
|
|
struct Utimbuf
|
|
{
|
|
Time_t actime;
|
|
Time_t modtime;
|
|
}
|
|
|
|
extern fn int utime(char* filename, void* times) @if(!env::WIN32);
|
|
extern fn int _wutime(WChar* filename, void* times) @if(env::WIN32);
|
|
|
|
fn void? native_set_modified_time(String filename, libc::Time_t time) => @stack_mem(256; Allocator smem)
|
|
{
|
|
Utimbuf times = { time, time };
|
|
$if env::WIN32:
|
|
if (_wutime(filename.to_wstring(smem)!, ×)) return io::GENERAL_ERROR~;
|
|
$else
|
|
if (utime(filename.zstr_copy(smem), ×)) return io::GENERAL_ERROR~;
|
|
$endif
|
|
}
|