Merge branch 'master' into tlsf-allocator

This commit is contained in:
Jeroen van Rijn
2024-06-09 16:42:23 +02:00
100 changed files with 2649 additions and 999 deletions
+1 -1
View File
@@ -1495,7 +1495,7 @@ fmt_pointer :: proc(fi: ^Info, p: rawptr, verb: rune) {
u := u64(uintptr(p))
switch verb {
case 'p', 'v', 'w':
if !fi.hash && verb == 'v' {
if !fi.hash {
io.write_string(fi.writer, "0x", &fi.n)
}
_fmt_int(fi, u, 16, false, 8*size_of(rawptr), __DIGITS_UPPER)
+1
View File
@@ -1,5 +1,6 @@
//+build !freestanding
//+build !js
//+build !orca
package fmt
import "base:runtime"
+746
View File
@@ -0,0 +1,746 @@
// package bmp implements a Microsoft BMP image reader
package core_image_bmp
import "core:image"
import "core:bytes"
import "core:compress"
import "core:mem"
import "base:intrinsics"
import "base:runtime"
Error :: image.Error
Image :: image.Image
Options :: image.Options
RGB_Pixel :: image.RGB_Pixel
RGBA_Pixel :: image.RGBA_Pixel
FILE_HEADER_SIZE :: 14
INFO_STUB_SIZE :: FILE_HEADER_SIZE + size_of(image.BMP_Version)
save_to_buffer :: proc(output: ^bytes.Buffer, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
if img == nil {
return .Invalid_Input_Image
}
if output == nil {
return .Invalid_Output
}
pixels := img.width * img.height
if pixels == 0 || pixels > image.MAX_DIMENSIONS {
return .Invalid_Input_Image
}
// While the BMP spec (and our loader) support more fanciful image types,
// `bmp.save` supports only 3 and 4 channel images with a bit depth of 8.
if img.depth != 8 || img.channels < 3 || img.channels > 4 {
return .Invalid_Input_Image
}
if img.channels * pixels != len(img.pixels.buf) {
return .Invalid_Input_Image
}
// Calculate and allocate size.
header_size := u32le(image.BMP_Version.V3)
total_header_size := header_size + 14 // file header = 14
pixel_count_bytes := u32le(align4(img.width * img.channels) * img.height)
header := image.BMP_Header{
// File header
magic = .Bitmap,
size = total_header_size + pixel_count_bytes,
_res1 = 0,
_res2 = 0,
pixel_offset = total_header_size,
// V3
info_size = .V3,
width = i32le(img.width),
height = i32le(img.height),
planes = 1,
bpp = u16le(8 * img.channels),
compression = .RGB,
image_size = pixel_count_bytes,
pels_per_meter = {2835, 2835}, // 72 DPI
colors_used = 0,
colors_important = 0,
}
written := 0
if resize(&output.buf, int(header.size)) != nil {
return .Unable_To_Allocate_Or_Resize
}
header_bytes := transmute([size_of(image.BMP_Header)]u8)header
written += int(total_header_size)
copy(output.buf[:], header_bytes[:written])
switch img.channels {
case 3:
row_bytes := img.width * img.channels
row_padded := align4(row_bytes)
pixels := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
for y in 0..<img.height {
row_offset := row_padded * (img.height - y - 1) + written
for x in 0..<img.width {
pix_offset := 3 * x
output.buf[row_offset + pix_offset + 0] = pixels[0].b
output.buf[row_offset + pix_offset + 1] = pixels[0].g
output.buf[row_offset + pix_offset + 2] = pixels[0].r
pixels = pixels[1:]
}
}
case 4:
row_bytes := img.width * img.channels
pixels := mem.slice_data_cast([]RGBA_Pixel, img.pixels.buf[:])
for y in 0..<img.height {
row_offset := row_bytes * (img.height - y - 1) + written
for x in 0..<img.width {
pix_offset := 4 * x
output.buf[row_offset + pix_offset + 0] = pixels[0].b
output.buf[row_offset + pix_offset + 1] = pixels[0].g
output.buf[row_offset + pix_offset + 2] = pixels[0].r
output.buf[row_offset + pix_offset + 3] = pixels[0].a
pixels = pixels[1:]
}
}
}
return
}
load_from_bytes :: proc(data: []byte, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
ctx := &compress.Context_Memory_Input{
input_data = data,
}
img, err = load_from_context(ctx, options, allocator)
return img, err
}
@(optimization_mode="speed")
load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
options := options
// For compress.read_slice(), until that's rewritten to not use temp allocator
runtime.DEFAULT_TEMP_ALLOCATOR_TEMP_GUARD()
if .info in options {
options |= {.return_metadata, .do_not_decompress_image}
options -= {.info}
}
if .return_header in options && .return_metadata in options {
options -= {.return_header}
}
info_buf: [size_of(image.BMP_Header)]u8
// Read file header (14) + info size (4)
stub_data := compress.read_slice(ctx, INFO_STUB_SIZE) or_return
copy(info_buf[:], stub_data[:])
stub_info := transmute(image.BMP_Header)info_buf
if stub_info.magic != .Bitmap {
for v in image.BMP_Magic {
if stub_info.magic == v {
return img, .Unsupported_OS2_File
}
}
return img, .Invalid_Signature
}
info: image.BMP_Header
switch stub_info.info_size {
case .OS2_v1:
// Read the remainder of the header
os2_data := compress.read_data(ctx, image.OS2_Header) or_return
info = transmute(image.BMP_Header)info_buf
info.width = i32le(os2_data.width)
info.height = i32le(os2_data.height)
info.planes = os2_data.planes
info.bpp = os2_data.bpp
switch info.bpp {
case 1, 4, 8, 24:
case:
return img, .Unsupported_BPP
}
case .ABBR_16 ..= .V5:
// Sizes include V3, V4, V5 and OS2v2 outright, but can also handle truncated headers.
// Sometimes called BITMAPV2INFOHEADER or BITMAPV3INFOHEADER.
// Let's just try to process it.
to_read := int(stub_info.info_size) - size_of(image.BMP_Version)
info_data := compress.read_slice(ctx, to_read) or_return
copy(info_buf[INFO_STUB_SIZE:], info_data[:])
// Update info struct with the rest of the data we read
info = transmute(image.BMP_Header)info_buf
case:
return img, .Unsupported_BMP_Version
}
/* TODO(Jeroen): Add a "strict" option to catch these non-issues that violate spec?
if info.planes != 1 {
return img, .Invalid_Planes_Value
}
*/
if img == nil {
img = new(Image)
}
img.which = .BMP
img.metadata = new_clone(image.BMP_Info{
info = info,
})
img.width = abs(int(info.width))
img.height = abs(int(info.height))
img.channels = 3
img.depth = 8
if img.width == 0 || img.height == 0 {
return img, .Invalid_Image_Dimensions
}
total_pixels := abs(img.width * img.height)
if total_pixels > image.MAX_DIMENSIONS {
return img, .Image_Dimensions_Too_Large
}
// TODO(Jeroen): Handle RGBA.
switch info.compression {
case .Bit_Fields, .Alpha_Bit_Fields:
switch info.bpp {
case 16, 32:
make_output(img, allocator) or_return
decode_rgb(ctx, img, info, allocator) or_return
case:
if is_os2(info.info_size) {
return img, .Unsupported_Compression
}
return img, .Unsupported_BPP
}
case .RGB:
make_output(img, allocator) or_return
decode_rgb(ctx, img, info, allocator) or_return
case .RLE4, .RLE8:
make_output(img, allocator) or_return
decode_rle(ctx, img, info, allocator) or_return
case .CMYK, .CMYK_RLE4, .CMYK_RLE8: fallthrough
case .PNG, .JPEG: fallthrough
case: return img, .Unsupported_Compression
}
// Flipped vertically
if info.height < 0 {
pixels := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
for y in 0..<img.height / 2 {
for x in 0..<img.width {
top := y * img.width + x
bot := (img.height - y - 1) * img.width + x
pixels[top], pixels[bot] = pixels[bot], pixels[top]
}
}
}
return
}
is_os2 :: proc(version: image.BMP_Version) -> (res: bool) {
#partial switch version {
case .OS2_v1, .OS2_v2: return true
case: return false
}
}
make_output :: proc(img: ^Image, allocator := context.allocator) -> (err: Error) {
assert(img != nil)
bytes_needed := img.channels * img.height * img.width
img.pixels.buf = make([dynamic]u8, bytes_needed, allocator)
if len(img.pixels.buf) != bytes_needed {
return .Unable_To_Allocate_Or_Resize
}
return
}
write :: proc(img: ^Image, x, y: int, pix: RGB_Pixel) -> (err: Error) {
if y >= img.height || x >= img.width {
return .Corrupt
}
out := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
assert(img.height >= 1 && img.width >= 1)
out[(img.height - y - 1) * img.width + x] = pix
return
}
Bitmask :: struct {
mask: [4]u32le `fmt:"b"`,
shift: [4]u32le,
bits: [4]u32le,
}
read_or_make_bit_masks :: proc(ctx: ^$C, info: image.BMP_Header) -> (res: Bitmask, read: int, err: Error) {
ctz :: intrinsics.count_trailing_zeros
c1s :: intrinsics.count_ones
#partial switch info.compression {
case .RGB:
switch info.bpp {
case 16:
return {
mask = {31 << 10, 31 << 5, 31, 0},
shift = { 10, 5, 0, 0},
bits = { 5, 5, 5, 0},
}, int(4 * info.colors_used), nil
case 32:
return {
mask = {255 << 16, 255 << 8, 255, 255 << 24},
shift = { 16, 8, 0, 24},
bits = { 8, 8, 8, 8},
}, int(4 * info.colors_used), nil
case: return {}, 0, .Unsupported_BPP
}
case .Bit_Fields, .Alpha_Bit_Fields:
bf := info.masks
alpha_mask := false
bit_count: u32le
#partial switch info.info_size {
case .ABBR_52 ..= .V5:
// All possible BMP header sizes 52+ bytes long, includes V4 + V5
// Bit fields were read as part of the header
// V3 header is 40 bytes. We need 56 at a minimum for RGBA bit fields in the next section.
if info.info_size >= .ABBR_56 {
alpha_mask = true
}
case .V3:
// Version 3 doesn't have a bit field embedded, but can still have a 3 or 4 color bit field.
// Because it wasn't read as part of the header, we need to read it now.
if info.compression == .Alpha_Bit_Fields {
bf = compress.read_data(ctx, [4]u32le) or_return
alpha_mask = true
read = 16
} else {
bf.xyz = compress.read_data(ctx, [3]u32le) or_return
read = 12
}
case:
// Bit fields are unhandled for this BMP version
return {}, 0, .Bitfield_Version_Unhandled
}
if alpha_mask {
res = {
mask = {bf.r, bf.g, bf.b, bf.a},
shift = {ctz(bf.r), ctz(bf.g), ctz(bf.b), ctz(bf.a)},
bits = {c1s(bf.r), c1s(bf.g), c1s(bf.b), c1s(bf.a)},
}
bit_count = res.bits.r + res.bits.g + res.bits.b + res.bits.a
} else {
res = {
mask = {bf.r, bf.g, bf.b, 0},
shift = {ctz(bf.r), ctz(bf.g), ctz(bf.b), 0},
bits = {c1s(bf.r), c1s(bf.g), c1s(bf.b), 0},
}
bit_count = res.bits.r + res.bits.g + res.bits.b
}
if bit_count > u32le(info.bpp) {
err = .Bitfield_Sum_Exceeds_BPP
}
overlapped := res.mask.r | res.mask.g | res.mask.b | res.mask.a
if c1s(overlapped) < bit_count {
err = .Bitfield_Overlapped
}
return res, read, err
case:
return {}, 0, .Unsupported_Compression
}
return
}
scale :: proc(val: $T, mask, shift, bits: u32le) -> (res: u8) {
if bits == 0 { return 0 } // Guard against malformed bit fields
v := (u32le(val) & mask) >> shift
mask_in := u32le(1 << bits) - 1
return u8(v * 255 / mask_in)
}
decode_rgb :: proc(ctx: ^$C, img: ^Image, info: image.BMP_Header, allocator := context.allocator) -> (err: Error) {
pixel_offset := int(info.pixel_offset)
pixel_offset -= int(info.info_size) + FILE_HEADER_SIZE
palette: [256]RGBA_Pixel
// Palette size is info.colors_used if populated. If not it's min(1 << bpp, offset to the pixels / channel count)
colors_used := min(256, 1 << info.bpp if info.colors_used == 0 else info.colors_used)
max_colors := pixel_offset / 3 if info.info_size == .OS2_v1 else pixel_offset / 4
colors_used = min(colors_used, u32le(max_colors))
switch info.bpp {
case 1:
if info.info_size == .OS2_v1 {
// 2 x RGB palette of instead of variable RGBA palette
for i in 0..<colors_used {
palette[i].rgb = image.read_data(ctx, RGB_Pixel) or_return
}
pixel_offset -= int(3 * colors_used)
} else {
for i in 0..<colors_used {
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
}
pixel_offset -= int(4 * colors_used)
}
skip_space(ctx, pixel_offset)
stride := (img.width + 7) / 8
for y in 0..<img.height {
data := compress.read_slice(ctx, stride) or_return
for x in 0..<img.width {
shift := u8(7 - (x & 0x07))
p := (data[x / 8] >> shift) & 0x01
write(img, x, y, palette[p].bgr) or_return
}
}
case 2: // Non-standard on modern Windows, but was allowed on WinCE
for i in 0..<colors_used {
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
}
pixel_offset -= int(4 * colors_used)
skip_space(ctx, pixel_offset)
stride := (img.width + 3) / 4
for y in 0..<img.height {
data := compress.read_slice(ctx, stride) or_return
for x in 0..<img.width {
shift := 6 - (x & 0x03) << 1
p := (data[x / 4] >> u8(shift)) & 0x03
write(img, x, y, palette[p].bgr) or_return
}
}
case 4:
if info.info_size == .OS2_v1 {
// 16 x RGB palette of instead of variable RGBA palette
for i in 0..<colors_used {
palette[i].rgb = image.read_data(ctx, RGB_Pixel) or_return
}
pixel_offset -= int(3 * colors_used)
} else {
for i in 0..<colors_used {
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
}
pixel_offset -= int(4 * colors_used)
}
skip_space(ctx, pixel_offset)
stride := (img.width + 1) / 2
for y in 0..<img.height {
data := compress.read_slice(ctx, stride) or_return
for x in 0..<img.width {
p := data[x / 2] >> 4 if x & 1 == 0 else data[x / 2]
write(img, x, y, palette[p & 0x0f].bgr) or_return
}
}
case 8:
if info.info_size == .OS2_v1 {
// 256 x RGB palette of instead of variable RGBA palette
for i in 0..<colors_used {
palette[i].rgb = image.read_data(ctx, RGB_Pixel) or_return
}
pixel_offset -= int(3 * colors_used)
} else {
for i in 0..<colors_used {
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
}
pixel_offset -= int(4 * colors_used)
}
skip_space(ctx, pixel_offset)
stride := align4(img.width)
for y in 0..<img.height {
data := compress.read_slice(ctx, stride) or_return
for x in 0..<img.width {
write(img, x, y, palette[data[x]].bgr) or_return
}
}
case 16:
bm, read := read_or_make_bit_masks(ctx, info) or_return
// Skip optional palette and other data
pixel_offset -= read
skip_space(ctx, pixel_offset)
stride := align4(img.width * 2)
for y in 0..<img.height {
data := compress.read_slice(ctx, stride) or_return
pixels := mem.slice_data_cast([]u16le, data)
for x in 0..<img.width {
v := pixels[x]
r := scale(v, bm.mask.r, bm.shift.r, bm.bits.r)
g := scale(v, bm.mask.g, bm.shift.g, bm.bits.g)
b := scale(v, bm.mask.b, bm.shift.b, bm.bits.b)
write(img, x, y, RGB_Pixel{r, g, b}) or_return
}
}
case 24:
// Eat useless palette and other padding
skip_space(ctx, pixel_offset)
stride := align4(img.width * 3)
for y in 0..<img.height {
data := compress.read_slice(ctx, stride) or_return
pixels := mem.slice_data_cast([]RGB_Pixel, data)
for x in 0..<img.width {
write(img, x, y, pixels[x].bgr) or_return
}
}
case 32:
bm, read := read_or_make_bit_masks(ctx, info) or_return
// Skip optional palette and other data
pixel_offset -= read
skip_space(ctx, pixel_offset)
for y in 0..<img.height {
data := compress.read_slice(ctx, img.width * size_of(RGBA_Pixel)) or_return
pixels := mem.slice_data_cast([]u32le, data)
for x in 0..<img.width {
v := pixels[x]
r := scale(v, bm.mask.r, bm.shift.r, bm.bits.r)
g := scale(v, bm.mask.g, bm.shift.g, bm.bits.g)
b := scale(v, bm.mask.b, bm.shift.b, bm.bits.b)
write(img, x, y, RGB_Pixel{r, g, b}) or_return
}
}
case:
return .Unsupported_BPP
}
return nil
}
decode_rle :: proc(ctx: ^$C, img: ^Image, info: image.BMP_Header, allocator := context.allocator) -> (err: Error) {
pixel_offset := int(info.pixel_offset)
pixel_offset -= int(info.info_size) + FILE_HEADER_SIZE
bytes_needed := size_of(RGB_Pixel) * img.height * img.width
if resize(&img.pixels.buf, bytes_needed) != nil {
return .Unable_To_Allocate_Or_Resize
}
out := mem.slice_data_cast([]RGB_Pixel, img.pixels.buf[:])
assert(len(out) == img.height * img.width)
palette: [256]RGBA_Pixel
switch info.bpp {
case 4:
colors_used := info.colors_used if info.colors_used > 0 else 16
colors_used = min(colors_used, 16)
for i in 0..<colors_used {
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
pixel_offset -= size_of(RGBA_Pixel)
}
skip_space(ctx, pixel_offset)
pixel_size := info.size - info.pixel_offset
remaining := compress.input_size(ctx) or_return
if remaining < i64(pixel_size) {
return .Corrupt
}
data := make([]u8, int(pixel_size) + 4)
defer delete(data)
for i in 0..<pixel_size {
data[i] = image.read_u8(ctx) or_return
}
y, x := 0, 0
index := 0
for {
if len(data[index:]) < 2 {
return .Corrupt
}
if data[index] > 0 {
for count in 0..<data[index] {
if count & 1 == 1 {
write(img, x, y, palette[(data[index + 1] >> 0) & 0x0f].bgr)
} else {
write(img, x, y, palette[(data[index + 1] >> 4) & 0x0f].bgr)
}
x += 1
}
index += 2
} else {
switch data[index + 1] {
case 0: // EOL
x = 0; y += 1
index += 2
case 1: // EOB
return
case 2: // MOVE
x += int(data[index + 2])
y += int(data[index + 3])
index += 4
case: // Literals
run_length := int(data[index + 1])
aligned := (align4(run_length) >> 1) + 2
if index + aligned >= len(data) {
return .Corrupt
}
for count in 0..<run_length {
val := data[index + 2 + count / 2]
if count & 1 == 1 {
val &= 0xf
} else {
val = val >> 4
}
write(img, x, y, palette[val].bgr)
x += 1
}
index += aligned
}
}
}
case 8:
colors_used := info.colors_used if info.colors_used > 0 else 256
colors_used = min(colors_used, 256)
for i in 0..<colors_used {
palette[i] = image.read_data(ctx, RGBA_Pixel) or_return
pixel_offset -= size_of(RGBA_Pixel)
}
skip_space(ctx, pixel_offset)
pixel_size := info.size - info.pixel_offset
remaining := compress.input_size(ctx) or_return
if remaining < i64(pixel_size) {
return .Corrupt
}
data := make([]u8, int(pixel_size) + 4)
defer delete(data)
for i in 0..<pixel_size {
data[i] = image.read_u8(ctx) or_return
}
y, x := 0, 0
index := 0
for {
if len(data[index:]) < 2 {
return .Corrupt
}
if data[index] > 0 {
for _ in 0..<data[index] {
write(img, x, y, palette[data[index + 1]].bgr)
x += 1
}
index += 2
} else {
switch data[index + 1] {
case 0: // EOL
x = 0; y += 1
index += 2
case 1: // EOB
return
case 2: // MOVE
x += int(data[index + 2])
y += int(data[index + 3])
index += 4
case: // Literals
run_length := int(data[index + 1])
aligned := align2(run_length) + 2
if index + aligned >= len(data) {
return .Corrupt
}
for count in 0..<run_length {
write(img, x, y, palette[data[index + 2 + count]].bgr)
x += 1
}
index += aligned
}
}
}
case:
return .Unsupported_BPP
}
return nil
}
align2 :: proc(width: int) -> (stride: int) {
stride = width
if width & 1 != 0 {
stride += 2 - (width & 1)
}
return
}
align4 :: proc(width: int) -> (stride: int) {
stride = width
if width & 3 != 0 {
stride += 4 - (width & 3)
}
return
}
skip_space :: proc(ctx: ^$C, bytes_to_skip: int) -> (err: Error) {
if bytes_to_skip < 0 {
return .Corrupt
}
for _ in 0..<bytes_to_skip {
image.read_u8(ctx) or_return
}
return
}
// Cleanup of image-specific data.
destroy :: proc(img: ^Image) {
if img == nil {
// Nothing to do. Load must've returned with an error.
return
}
bytes.buffer_destroy(&img.pixels)
if v, ok := img.metadata.(^image.BMP_Info); ok {
free(v)
}
free(img)
}
@(init, private)
_register :: proc() {
image.register(.BMP, load_from_bytes, destroy)
}
+4
View File
@@ -0,0 +1,4 @@
//+build js
package core_image_bmp
load :: proc{load_from_bytes, load_from_context}
+34
View File
@@ -0,0 +1,34 @@
//+build !js
package core_image_bmp
import "core:os"
import "core:bytes"
load :: proc{load_from_file, load_from_bytes, load_from_context}
load_from_file :: proc(filename: string, options := Options{}, allocator := context.allocator) -> (img: ^Image, err: Error) {
context.allocator = allocator
data, ok := os.read_entire_file(filename)
defer delete(data)
if ok {
return load_from_bytes(data, options)
} else {
return nil, .Unable_To_Read_File
}
}
save :: proc{save_to_buffer, save_to_file}
save_to_file :: proc(output: string, img: ^Image, options := Options{}, allocator := context.allocator) -> (err: Error) {
context.allocator = allocator
out := &bytes.Buffer{}
defer bytes.buffer_destroy(out)
save_to_buffer(out, img, options) or_return
write_ok := os.write_entire_file(output, out.buf[:])
return nil if write_ok else .Unable_To_Write_File
}
+126
View File
@@ -12,6 +12,7 @@ package image
import "core:bytes"
import "core:mem"
import "core:io"
import "core:compress"
import "base:runtime"
@@ -62,6 +63,7 @@ Image_Metadata :: union #shared_nil {
^PNG_Info,
^QOI_Info,
^TGA_Info,
^BMP_Info,
}
@@ -159,11 +161,13 @@ Error :: union #shared_nil {
Netpbm_Error,
PNG_Error,
QOI_Error,
BMP_Error,
compress.Error,
compress.General_Error,
compress.Deflate_Error,
compress.ZLIB_Error,
io.Error,
runtime.Allocator_Error,
}
@@ -196,6 +200,128 @@ General_Image_Error :: enum {
Unable_To_Allocate_Or_Resize,
}
/*
BMP-specific
*/
BMP_Error :: enum {
None = 0,
Invalid_File_Size,
Unsupported_BMP_Version,
Unsupported_OS2_File,
Unsupported_Compression,
Unsupported_BPP,
Invalid_Stride,
Invalid_Color_Count,
Implausible_File_Size,
Bitfield_Version_Unhandled, // We don't (yet) handle bit fields for this BMP version.
Bitfield_Sum_Exceeds_BPP, // Total mask bit count > bpp
Bitfield_Overlapped, // Channel masks overlap
}
// img.metadata is wrapped in a struct in case we need to add to it later
// without putting it in BMP_Header
BMP_Info :: struct {
info: BMP_Header,
}
BMP_Magic :: enum u16le {
Bitmap = 0x4d42, // 'BM'
OS2_Bitmap_Array = 0x4142, // 'BA'
OS2_Icon = 0x4349, // 'IC',
OS2_Color_Icon = 0x4943, // 'CI'
OS2_Pointer = 0x5450, // 'PT'
OS2_Color_Pointer = 0x5043, // 'CP'
}
// See: http://justsolve.archiveteam.org/wiki/BMP#Well-known_versions
BMP_Version :: enum u32le {
OS2_v1 = 12, // BITMAPCOREHEADER (Windows V2 / OS/2 version 1.0)
OS2_v2 = 64, // BITMAPCOREHEADER2 (OS/2 version 2.x)
V3 = 40, // BITMAPINFOHEADER
V4 = 108, // BITMAPV4HEADER
V5 = 124, // BITMAPV5HEADER
ABBR_16 = 16, // Abbreviated
ABBR_24 = 24, // ..
ABBR_48 = 48, // ..
ABBR_52 = 52, // ..
ABBR_56 = 56, // ..
}
BMP_Header :: struct #packed {
// File header
magic: BMP_Magic,
size: u32le,
_res1: u16le, // Reserved; must be zero
_res2: u16le, // Reserved; must be zero
pixel_offset: u32le, // Offset in bytes, from the beginning of BMP_Header to the pixel data
// V3
info_size: BMP_Version,
width: i32le,
height: i32le,
planes: u16le,
bpp: u16le,
compression: BMP_Compression,
image_size: u32le,
pels_per_meter: [2]u32le,
colors_used: u32le,
colors_important: u32le, // OS2_v2 is equal up to here
// V4
masks: [4]u32le `fmt:"32b"`,
colorspace: BMP_Logical_Color_Space,
endpoints: BMP_CIEXYZTRIPLE,
gamma: [3]BMP_GAMMA16_16,
// V5
intent: BMP_Gamut_Mapping_Intent,
profile_data: u32le,
profile_size: u32le,
reserved: u32le,
}
#assert(size_of(BMP_Header) == 138)
OS2_Header :: struct #packed {
// BITMAPCOREHEADER minus info_size field
width: i16le,
height: i16le,
planes: u16le,
bpp: u16le,
}
#assert(size_of(OS2_Header) == 8)
BMP_Compression :: enum u32le {
RGB = 0x0000,
RLE8 = 0x0001,
RLE4 = 0x0002,
Bit_Fields = 0x0003, // If Windows
Huffman1D = 0x0003, // If OS2v2
JPEG = 0x0004, // If Windows
RLE24 = 0x0004, // If OS2v2
PNG = 0x0005,
Alpha_Bit_Fields = 0x0006,
CMYK = 0x000B,
CMYK_RLE8 = 0x000C,
CMYK_RLE4 = 0x000D,
}
BMP_Logical_Color_Space :: enum u32le {
CALIBRATED_RGB = 0x00000000,
sRGB = 0x73524742, // 'sRGB'
WINDOWS_COLOR_SPACE = 0x57696E20, // 'Win '
}
BMP_FXPT2DOT30 :: u32le
BMP_CIEXYZ :: [3]BMP_FXPT2DOT30
BMP_CIEXYZTRIPLE :: [3]BMP_CIEXYZ
BMP_GAMMA16_16 :: [2]u16le
BMP_Gamut_Mapping_Intent :: enum u32le {
INVALID = 0x00000000, // If not V5, this field will just be zero-initialized and not valid.
ABS_COLORIMETRIC = 0x00000008,
BUSINESS = 0x00000001,
GRAPHICS = 0x00000002,
IMAGES = 0x00000004,
}
/*
Netpbm-specific definitions
*/
+1 -1
View File
@@ -350,7 +350,7 @@ _reduce_pi_f64 :: proc "contextless" (x: f64) -> f64 #no_bounds_check {
// that is, 1/PI = SUM bdpi[i]*2^(-64*i).
// 19 64-bit digits give 1216 bits of precision
// to handle the largest possible f64 exponent.
@static bdpi := [?]u64{
@(static, rodata) bdpi := [?]u64{
0x0000000000000000,
0x517cc1b727220a94,
0xfe13abe8fa9a6ee0,
+9 -9
View File
@@ -130,10 +130,10 @@ pow10 :: proc{
@(require_results)
pow10_f16 :: proc "contextless" (n: f16) -> f16 {
@static pow10_pos_tab := [?]f16{
@(static, rodata) pow10_pos_tab := [?]f16{
1e00, 1e01, 1e02, 1e03, 1e04,
}
@static pow10_neg_tab := [?]f16{
@(static, rodata) pow10_neg_tab := [?]f16{
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07,
}
@@ -151,13 +151,13 @@ pow10_f16 :: proc "contextless" (n: f16) -> f16 {
@(require_results)
pow10_f32 :: proc "contextless" (n: f32) -> f32 {
@static pow10_pos_tab := [?]f32{
@(static, rodata) pow10_pos_tab := [?]f32{
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1e30, 1e31, 1e32, 1e33, 1e34, 1e35, 1e36, 1e37, 1e38,
}
@static pow10_neg_tab := [?]f32{
@(static, rodata) pow10_neg_tab := [?]f32{
1e-00, 1e-01, 1e-02, 1e-03, 1e-04, 1e-05, 1e-06, 1e-07, 1e-08, 1e-09,
1e-10, 1e-11, 1e-12, 1e-13, 1e-14, 1e-15, 1e-16, 1e-17, 1e-18, 1e-19,
1e-20, 1e-21, 1e-22, 1e-23, 1e-24, 1e-25, 1e-26, 1e-27, 1e-28, 1e-29,
@@ -179,16 +179,16 @@ pow10_f32 :: proc "contextless" (n: f32) -> f32 {
@(require_results)
pow10_f64 :: proc "contextless" (n: f64) -> f64 {
@static pow10_tab := [?]f64{
@(static, rodata) pow10_tab := [?]f64{
1e00, 1e01, 1e02, 1e03, 1e04, 1e05, 1e06, 1e07, 1e08, 1e09,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22, 1e23, 1e24, 1e25, 1e26, 1e27, 1e28, 1e29,
1e30, 1e31,
}
@static pow10_pos_tab32 := [?]f64{
@(static, rodata) pow10_pos_tab32 := [?]f64{
1e00, 1e32, 1e64, 1e96, 1e128, 1e160, 1e192, 1e224, 1e256, 1e288,
}
@static pow10_neg_tab32 := [?]f64{
@(static, rodata) pow10_neg_tab32 := [?]f64{
1e-00, 1e-32, 1e-64, 1e-96, 1e-128, 1e-160, 1e-192, 1e-224, 1e-256, 1e-288, 1e-320,
}
@@ -1274,7 +1274,7 @@ binomial :: proc "contextless" (n, k: int) -> int {
@(require_results)
factorial :: proc "contextless" (n: int) -> int {
when size_of(int) == size_of(i64) {
@static table := [21]int{
@(static, rodata) table := [21]int{
1,
1,
2,
@@ -1298,7 +1298,7 @@ factorial :: proc "contextless" (n: int) -> int {
2_432_902_008_176_640_000,
}
} else {
@static table := [13]int{
@(static, rodata) table := [13]int{
1,
1,
2,
+3 -3
View File
@@ -67,7 +67,7 @@ package math
// masks any imprecision in the polynomial.
@(private="file", require_results)
stirling :: proc "contextless" (x: f64) -> (f64, f64) {
@(static) gamS := [?]f64{
@(static, rodata) gamS := [?]f64{
+7.87311395793093628397e-04,
-2.29549961613378126380e-04,
-2.68132617805781232825e-03,
@@ -103,7 +103,7 @@ gamma_f64 :: proc "contextless" (x: f64) -> f64 {
return false
}
@(static) gamP := [?]f64{
@(static, rodata) gamP := [?]f64{
1.60119522476751861407e-04,
1.19135147006586384913e-03,
1.04213797561761569935e-02,
@@ -112,7 +112,7 @@ gamma_f64 :: proc "contextless" (x: f64) -> f64 {
4.94214826801497100753e-01,
9.99999999999999996796e-01,
}
@(static) gamQ := [?]f64{
@(static, rodata) gamQ := [?]f64{
-2.31581873324120129819e-05,
+5.39605580493303397842e-04,
-4.45641913851797240494e-03,
+7 -7
View File
@@ -123,7 +123,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
return -x
}
@static lgamA := [?]f64{
@(static, rodata) lgamA := [?]f64{
0h3FB3C467E37DB0C8,
0h3FD4A34CC4A60FAD,
0h3FB13E001A5562A7,
@@ -137,7 +137,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
0h3EFA7074428CFA52,
0h3F07858E90A45837,
}
@static lgamR := [?]f64{
@(static, rodata) lgamR := [?]f64{
1.0,
0h3FF645A762C4AB74,
0h3FE71A1893D3DCDC,
@@ -146,7 +146,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
0h3F497DDACA41A95B,
0h3EDEBAF7A5B38140,
}
@static lgamS := [?]f64{
@(static, rodata) lgamS := [?]f64{
0hBFB3C467E37DB0C8,
0h3FCB848B36E20878,
0h3FD4D98F4F139F59,
@@ -155,7 +155,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
0h3F5E26B67368F239,
0h3F00BFECDD17E945,
}
@static lgamT := [?]f64{
@(static, rodata) lgamT := [?]f64{
0h3FDEF72BC8EE38A2,
0hBFC2E4278DC6C509,
0h3FB08B4294D5419B,
@@ -172,7 +172,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
0hBF347F24ECC38C38,
0h3F35FD3EE8C2D3F4,
}
@static lgamU := [?]f64{
@(static, rodata) lgamU := [?]f64{
0hBFB3C467E37DB0C8,
0h3FE4401E8B005DFF,
0h3FF7475CD119BD6F,
@@ -180,7 +180,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
0h3FCD4EAEF6010924,
0h3F8B678BBF2BAB09,
}
@static lgamV := [?]f64{
@(static, rodata) lgamV := [?]f64{
1.0,
0h4003A5D7C2BD619C,
0h40010725A42B18F5,
@@ -188,7 +188,7 @@ lgamma_f64 :: proc "contextless" (x: f64) -> (lgamma: f64, sign: int) {
0h3FBAAE55D6537C88,
0h3F6A5ABB57D0CF61,
}
@static lgamW := [?]f64{
@(static, rodata) lgamW := [?]f64{
0h3FDACFE390C97D69,
0h3FB555555555553B,
0hBF66C16C16B02E5C,
+1 -1
View File
@@ -234,7 +234,7 @@ _trig_reduce_f64 :: proc "contextless" (x: f64) -> (j: u64, z: f64) #no_bounds_c
// that is, 4/pi = Sum bd_pi4[i]*2^(-64*i)
// 19 64-bit digits and the leading one bit give 1217 bits
// of precision to handle the largest possible f64 exponent.
@static bd_pi4 := [?]u64{
@(static, rodata) bd_pi4 := [?]u64{
0x0000000000000001,
0x45f306dc9c882a53,
0xf84eafa3ea69bb81,
+3 -3
View File
@@ -19,7 +19,7 @@ import "core:math"
exp_float64 :: proc(r: ^Rand = nil) -> f64 {
re :: 7.69711747013104972
@(static)
@(static, rodata)
ke := [256]u32{
0xe290a139, 0x0, 0x9beadebc, 0xc377ac71, 0xd4ddb990,
0xde893fb8, 0xe4a8e87c, 0xe8dff16a, 0xebf2deab, 0xee49a6e8,
@@ -74,7 +74,7 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 {
0xf7b577d2, 0xf69c650c, 0xf51530f0, 0xf2cb0e3c, 0xeeefb15d,
0xe6da6ecf,
}
@(static)
@(static, rodata)
we := [256]f32{
2.0249555e-09, 1.486674e-11, 2.4409617e-11, 3.1968806e-11,
3.844677e-11, 4.4228204e-11, 4.9516443e-11, 5.443359e-11,
@@ -141,7 +141,7 @@ exp_float64 :: proc(r: ^Rand = nil) -> f64 {
1.2393786e-09, 1.276585e-09, 1.3193139e-09, 1.3695435e-09,
1.4305498e-09, 1.508365e-09, 1.6160854e-09, 1.7921248e-09,
}
@(static)
@(static, rodata)
fe := [256]f32{
1, 0.9381437, 0.90046996, 0.87170434, 0.8477855, 0.8269933,
0.8084217, 0.7915276, 0.77595687, 0.7614634, 0.7478686,
+3 -3
View File
@@ -21,7 +21,7 @@ import "core:math"
norm_float64 :: proc(r: ^Rand = nil) -> f64 {
rn :: 3.442619855899
@(static)
@(static, rodata)
kn := [128]u32{
0x76ad2212, 0x00000000, 0x600f1b53, 0x6ce447a6, 0x725b46a2,
0x7560051d, 0x774921eb, 0x789a25bd, 0x799045c3, 0x7a4bce5d,
@@ -50,7 +50,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
0x7da61a1e, 0x7d72a0fb, 0x7d30e097, 0x7cd9b4ab, 0x7c600f1a,
0x7ba90bdc, 0x7a722176, 0x77d664e5,
}
@(static)
@(static, rodata)
wn := [128]f32{
1.7290405e-09, 1.2680929e-10, 1.6897518e-10, 1.9862688e-10,
2.2232431e-10, 2.4244937e-10, 2.601613e-10, 2.7611988e-10,
@@ -85,7 +85,7 @@ norm_float64 :: proc(r: ^Rand = nil) -> f64 {
1.2601323e-09, 1.2857697e-09, 1.3146202e-09, 1.347784e-09,
1.3870636e-09, 1.4357403e-09, 1.5008659e-09, 1.6030948e-09,
}
@(static)
@(static, rodata)
fn := [128]f32{
1.00000000, 0.9635997, 0.9362827, 0.9130436, 0.89228165,
0.87324303, 0.8555006, 0.8387836, 0.8229072, 0.8077383,
+5 -1
View File
@@ -87,8 +87,12 @@ read_dir :: proc(fd: Handle, n: int, allocator := context.allocator) -> (fi: []F
find_data := &win32.WIN32_FIND_DATAW{}
find_handle := win32.FindFirstFileW(raw_data(wpath_search), find_data)
if find_handle == win32.INVALID_HANDLE_VALUE {
err = Errno(win32.GetLastError())
return dfi[:], err
}
defer win32.FindClose(find_handle)
for n != 0 && find_handle != nil {
for n != 0 {
fi: File_Info
fi = find_data_to_file_info(path, find_data)
if fi.name != "" {
+1 -1
View File
@@ -111,7 +111,7 @@ next_random :: proc(r: ^[2]u64) -> u64 {
@(require_results)
random_string :: proc(buf: []byte) -> string {
@static digits := "0123456789"
@(static, rodata) digits := "0123456789"
u := next_random(&random_string_seed)
+6 -2
View File
@@ -5,7 +5,6 @@ foreign import libc "system:c"
import "base:runtime"
import "core:strings"
import "core:sys/unix"
import "core:c"
Handle :: distinct i32
@@ -328,6 +327,11 @@ foreign dl {
@(link_name="dlerror") _unix_dlerror :: proc() -> cstring ---
}
@(private)
foreign libc {
_lwp_self :: proc() -> i32 ---
}
// NOTE(phix): Perhaps share the following functions with FreeBSD if they turn out to be the same in the end.
is_path_separator :: proc(r: rune) -> bool {
@@ -721,7 +725,7 @@ exit :: proc "contextless" (code: int) -> ! {
}
current_thread_id :: proc "contextless" () -> int {
return cast(int) unix.pthread_self()
return int(_lwp_self())
}
dlopen :: proc(filename: string, flags: int) -> rawptr {
+1 -1
View File
@@ -375,7 +375,7 @@ decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64
return
}
@static power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
@(static, rodata) power_table := [?]int{1, 3, 6, 9, 13, 16, 19, 23, 26}
exp = 0
for d.decimal_point > 0 {
+10 -7
View File
@@ -882,13 +882,16 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
s = s[1:]
fallthrough
case 'i', 'I':
n = common_prefix_len_ignore_case(s, "infinity")
if 3 < n && n < 8 { // "inf" or "infinity"
n = 3
}
if n == 3 || n == 8 {
m := common_prefix_len_ignore_case(s, "infinity")
if 3 <= m && m < 9 { // "inf" to "infinity"
f = 0h7ff00000_00000000 if sign == 1 else 0hfff00000_00000000
n = nsign + 3
if m == 8 {
// We only count the entire prefix if it is precisely "infinity".
n = nsign + m
} else {
// The string was either only "inf" or incomplete.
n = nsign + 3
}
ok = true
return
}
@@ -1092,7 +1095,7 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) {
}
trunc_block: if !trunc {
@static pow10 := [?]f64{
@(static, rodata) pow10 := [?]f64{
1e0, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9,
1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19,
1e20, 1e21, 1e22,
+1 -1
View File
@@ -52,7 +52,7 @@ _futex_wait_with_timeout :: proc "contextless" (f: ^Futex, expected: u32, durati
}
} else {
timeout_ns := u32(duration) * 1000
timeout_ns := u32(duration)
s := __ulock_wait(UL_COMPARE_AND_WAIT | ULF_NO_ERRNO, f, u64(expected), timeout_ns)
if s >= 0 {
return true
+6 -2
View File
@@ -1,8 +1,12 @@
//+private
package sync
import "core:sys/unix"
foreign import libc "system:c"
foreign libc {
_lwp_self :: proc "c" () -> i32 ---
}
_current_thread_id :: proc "contextless" () -> int {
return cast(int) unix.pthread_self()
return int(_lwp_self())
}
+1
View File
@@ -527,6 +527,7 @@ macos_release_map: map[string]Darwin_To_Release = {
"23D60" = {{23, 3, 0}, "macOS", {"Sonoma", {14, 3, 1}}},
"23E214" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 0}}},
"23E224" = {{23, 4, 0}, "macOS", {"Sonoma", {14, 4, 1}}},
"23F79" = {{23, 5, 0}, "macOS", {"Sonoma", {14, 5, 0}}},
}
@(private)
+3
View File
@@ -53,6 +53,9 @@ get_log_level :: #force_inline proc() -> runtime.Logger_Level {
else when LOG_LEVEL == "warning" { return .Warning }
else when LOG_LEVEL == "error" { return .Error }
else when LOG_LEVEL == "fatal" { return .Fatal }
else {
#panic("Unknown `ODIN_TEST_LOG_LEVEL`: \"" + LOG_LEVEL + "\", possible levels are: \"debug\", \"info\", \"warning\", \"error\", or \"fatal\".")
}
}
}
+12 -11
View File
@@ -2,11 +2,11 @@
// +private
package thread
import "base:intrinsics"
import "core:sync"
import "core:sys/unix"
import "core:time"
CAS :: intrinsics.atomic_compare_exchange_strong
CAS :: sync.atomic_compare_exchange_strong
// NOTE(tetra): Aligned here because of core/unix/pthread_linux.odin/pthread_t.
// Also see core/sys/darwin/mach_darwin.odin/semaphore_t.
@@ -32,11 +32,13 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
t.id = sync.current_thread_id()
for (.Started not_in t.flags) {
sync.wait(&t.cond, &t.mutex)
for (.Started not_in sync.atomic_load(&t.flags)) {
// HACK: use a timeout so in the event that the condition is signalled at THIS comment's exact point
// (after checking flags, before starting the wait) it gets itself out of that deadlock after a ms.
sync.wait_with_timeout(&t.cond, &t.mutex, time.Millisecond)
}
if .Joined in t.flags {
if .Joined in sync.atomic_load(&t.flags) {
return nil
}
@@ -60,11 +62,11 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
t.procedure(t)
}
intrinsics.atomic_store(&t.flags, t.flags + { .Done })
sync.atomic_or(&t.flags, { .Done })
sync.unlock(&t.mutex)
if .Self_Cleanup in t.flags {
if .Self_Cleanup in sync.atomic_load(&t.flags) {
t.unix_thread = {}
// NOTE(ftphikari): It doesn't matter which context 'free' received, right?
context = {}
@@ -122,13 +124,12 @@ _create :: proc(procedure: Thread_Proc, priority: Thread_Priority) -> ^Thread {
}
_start :: proc(t: ^Thread) {
// sync.guard(&t.mutex)
t.flags += { .Started }
sync.atomic_or(&t.flags, { .Started })
sync.signal(&t.cond)
}
_is_done :: proc(t: ^Thread) -> bool {
return .Done in intrinsics.atomic_load(&t.flags)
return .Done in sync.atomic_load(&t.flags)
}
_join :: proc(t: ^Thread) {
@@ -139,7 +140,7 @@ _join :: proc(t: ^Thread) {
}
// Preserve other flags besides `.Joined`, like `.Started`.
unjoined := intrinsics.atomic_load(&t.flags) - {.Joined}
unjoined := sync.atomic_load(&t.flags) - {.Joined}
joined := unjoined + {.Joined}
// Try to set `t.flags` from unjoined to joined. If it returns joined,
+1
View File
@@ -389,6 +389,7 @@ is_leap_year :: proc "contextless" (year: int) -> (leap: bool) {
return year % 4 == 0 && (year % 100 != 0 || year % 400 == 0)
}
@(rodata)
days_before := [?]i32{
0,
31,
+24
View File
@@ -0,0 +1,24 @@
//+private
//+build orca
package time
_IS_SUPPORTED :: false
_now :: proc "contextless" () -> Time {
return {}
}
_sleep :: proc "contextless" (d: Duration) {
}
_tick_now :: proc "contextless" () -> Tick {
// mul_div_u64 :: proc "contextless" (val, num, den: i64) -> i64 {
// q := val / den
// r := val % den
// return q * num + r * num / den
// }
return {}
}
_yield :: proc "contextless" () {
}
+10
View File
@@ -12,6 +12,7 @@ package unicode
@(private) pLo :: pLl | pLu // a letter that is neither upper nor lower case.
@(private) pLmask :: pLo
@(rodata)
char_properties := [MAX_LATIN1+1]u8{
0x00 = pC, // '\x00'
0x01 = pC, // '\x01'
@@ -272,6 +273,7 @@ char_properties := [MAX_LATIN1+1]u8{
}
@(rodata)
alpha_ranges := [?]i32{
0x00d8, 0x00f6,
0x00f8, 0x01f5,
@@ -427,6 +429,7 @@ alpha_ranges := [?]i32{
0xffda, 0xffdc,
}
@(rodata)
alpha_singlets := [?]i32{
0x00aa,
0x00b5,
@@ -462,6 +465,7 @@ alpha_singlets := [?]i32{
0xfe74,
}
@(rodata)
space_ranges := [?]i32{
0x0009, 0x000d, // tab and newline
0x0020, 0x0020, // space
@@ -477,6 +481,7 @@ space_ranges := [?]i32{
0xfeff, 0xfeff,
}
@(rodata)
unicode_spaces := [?]i32{
0x0009, // tab
0x000a, // LF
@@ -494,6 +499,7 @@ unicode_spaces := [?]i32{
0xfeff, // unknown
}
@(rodata)
to_upper_ranges := [?]i32{
0x0061, 0x007a, 468, // a-z A-Z
0x00e0, 0x00f6, 468,
@@ -532,6 +538,7 @@ to_upper_ranges := [?]i32{
0xff41, 0xff5a, 468,
}
@(rodata)
to_upper_singlets := [?]i32{
0x00ff, 621,
0x0101, 499,
@@ -875,6 +882,7 @@ to_upper_singlets := [?]i32{
0x1ff3, 509,
}
@(rodata)
to_lower_ranges := [?]i32{
0x0041, 0x005a, 532, // A-Z a-z
0x00c0, 0x00d6, 532, // - -
@@ -914,6 +922,7 @@ to_lower_ranges := [?]i32{
0xff21, 0xff3a, 532, // - -
}
@(rodata)
to_lower_singlets := [?]i32{
0x0100, 501,
0x0102, 501,
@@ -1250,6 +1259,7 @@ to_lower_singlets := [?]i32{
0x1ffc, 491,
}
@(rodata)
to_title_singlets := [?]i32{
0x01c4, 501,
0x01c6, 499,