| 1 | module os |
| 2 | |
| 3 | // write_le writes an unsigned number value to the file. |
| 4 | // It assumes that the value should be stored in a *little endian order*. |
| 5 | // If the machine is a big endian one, it will first convert the big endian value to a little endian one. |
| 6 | // It is safe to use as a cross platform way to write data, for which you have to use a predefined order (defined in a file format spec). |
| 7 | pub fn (mut f File) write_le[T](data T) ! { |
| 8 | mut serialized := data |
| 9 | $if big_endian { |
| 10 | serialized = swap_bytes(serialized) |
| 11 | } |
| 12 | C.errno = 0 // needed for tcc |
| 13 | check_fwrite(C.fwrite(voidptr(&serialized), sizeof(T), 1, f.cfile))! |
| 14 | } |
| 15 | |
| 16 | // write_be writes an unsigned number value to the file. |
| 17 | // It assumes that the value should be stored in a *big endian order*. |
| 18 | // If the machine is a little endian one, it will first convert the little endian value to a big endian one. |
| 19 | // It is safe to use as a cross platform way to write data, for which you have to use a predefined order (defined in a file format spec). |
| 20 | pub fn (mut f File) write_be[T](data T) ! { |
| 21 | mut serialized := data |
| 22 | $if little_endian { |
| 23 | serialized = swap_bytes(serialized) |
| 24 | } |
| 25 | C.errno = 0 // needed for tcc |
| 26 | check_fwrite(C.fwrite(voidptr(&serialized), sizeof(T), 1, f.cfile))! |
| 27 | } |
| 28 | |
| 29 | // read_le reads an unsigned number value from the file. |
| 30 | // It assumes that the value was stored in a *little endian order*. |
| 31 | // If the machine is a big endian one, it will convert the value to a big endian one. |
| 32 | // It is intended to use as a cross platform way to read data, for which the order is known (from the file format spec). |
| 33 | pub fn (mut f File) read_le[T]() !T { |
| 34 | mut serialized := T(0) |
| 35 | C.errno = 0 // needed for tcc |
| 36 | check_fread(C.fread(voidptr(&serialized), sizeof(T), 1, f.cfile))! |
| 37 | $if big_endian { |
| 38 | return swap_bytes(serialized) |
| 39 | } |
| 40 | return serialized |
| 41 | } |
| 42 | |
| 43 | // read_be reads an unsigned number value from the file. |
| 44 | // It assumes that the value was stored in a *big endian order*. |
| 45 | // If the machine is a little endian one, it will convert the value to a little endian one. |
| 46 | // It is intended to use as a cross platform way to read data, for which the order is known (from the file format spec). |
| 47 | pub fn (mut f File) read_be[T]() !T { |
| 48 | mut serialized := T(0) |
| 49 | C.errno = 0 // needed for tcc |
| 50 | check_fread(C.fread(voidptr(&serialized), sizeof(T), 1, f.cfile))! |
| 51 | $if little_endian { |
| 52 | return swap_bytes(serialized) |
| 53 | } |
| 54 | return serialized |
| 55 | } |
| 56 | |
| 57 | // write_u8 writes a single byte value to the file `f`. |
| 58 | // Note: if possible, use some of the other APIs, that write larger chunks of data, before using write_u8/1. |
| 59 | pub fn (mut f File) write_u8(b u8) ! { |
| 60 | C.errno = 0 // needed for tcc |
| 61 | check_fwrite(C.fwrite(voidptr(&b), 1, 1, f.cfile))! |
| 62 | } |
| 63 | |
| 64 | // read_u8 reads a single byte value from the file `f`. |
| 65 | // Note: if possible, use some of the other APIs, that read larger chunks of data, before using read_u8/1. |
| 66 | pub fn (mut f File) read_u8() !u8 { |
| 67 | mut res := u8(0) |
| 68 | C.errno = 0 // needed for tcc |
| 69 | check_fread(C.fread(voidptr(&res), 1, 1, f.cfile))! |
| 70 | return res |
| 71 | } |
| 72 | |
| 73 | // private helpers |
| 74 | |
| 75 | @[inline] |
| 76 | fn swap_bytes_u16(x u16) u16 { |
| 77 | // vfmt off |
| 78 | return ((x >> 8) & 0x00FF) | |
| 79 | ((x << 8) & 0xFF00) |
| 80 | // vfmt on |
| 81 | } |
| 82 | |
| 83 | @[inline] |
| 84 | fn swap_bytes_u32(x u32) u32 { |
| 85 | // vfmt off |
| 86 | return ((x >> 24) & 0x0000_00FF) | |
| 87 | ((x >> 8) & 0x0000_FF00) | |
| 88 | ((x << 8) & 0x00FF_0000) | |
| 89 | ((x << 24) & 0xFF00_0000) |
| 90 | // vfmt on |
| 91 | } |
| 92 | |
| 93 | @[inline] |
| 94 | fn swap_bytes_u64(x u64) u64 { |
| 95 | // vfmt off |
| 96 | return ((x >> 56) & 0x00000000_000000FF) | |
| 97 | ((x >> 40) & 0x00000000_0000FF00) | |
| 98 | ((x >> 24) & 0x00000000_00FF0000) | |
| 99 | ((x >> 8) & 0x00000000_FF000000) | |
| 100 | ((x << 8) & 0x000000FF_00000000) | |
| 101 | ((x << 24) & 0x0000FF00_00000000) | |
| 102 | ((x << 40) & 0x00FF0000_00000000) | |
| 103 | ((x << 56) & 0xFF000000_00000000) |
| 104 | // vfmt on |
| 105 | } |
| 106 | |
| 107 | fn swap_bytes[T](input T) T { |
| 108 | $if T is u8 { |
| 109 | return input |
| 110 | } $else $if T is i8 { |
| 111 | return input |
| 112 | } $else $if T is u16 { |
| 113 | return swap_bytes_u16(input) |
| 114 | } $else $if T is u32 { |
| 115 | return swap_bytes_u32(input) |
| 116 | } $else $if T is u64 { |
| 117 | return swap_bytes_u64(input) |
| 118 | } $else $if T is i16 { |
| 119 | return i16(swap_bytes_u16(u16(input))) |
| 120 | } $else $if T is i32 { |
| 121 | return i32(swap_bytes_u32(u32(input))) |
| 122 | } $else $if T is int { |
| 123 | return i32(swap_bytes_u32(u32(input))) |
| 124 | } $else $if T is i64 { |
| 125 | return i64(swap_bytes_u64(u64(input))) |
| 126 | } $else { |
| 127 | panic('type is not supported: ' + typeof[T]().str()) |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | fn check_cf(x usize, label string) ! { |
| 132 | if C.errno != 0 { |
| 133 | return error(posix_get_error_msg(C.errno)) |
| 134 | } |
| 135 | if x == 0 { |
| 136 | return error(label) |
| 137 | } |
| 138 | } |
| 139 | |
| 140 | fn check_fwrite(x usize) ! { |
| 141 | check_cf(x, 'fwrite')! |
| 142 | } |
| 143 | |
| 144 | fn check_fread(x usize) ! { |
| 145 | check_cf(x, 'fread')! |
| 146 | } |
| 147 | |