Merge branch 'master' into tilde

This commit is contained in:
gingerBill
2023-08-03 13:14:09 +01:00
55 changed files with 1077 additions and 887 deletions
+1
View File
@@ -1,3 +1,4 @@
//+vet !using-param
package zlib
/*
+17 -20
View File
@@ -184,28 +184,26 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
advance :: proc(t: ^Tokenizer) -> (err: Error) {
if t == nil { return .Tokenizer_Is_Nil }
using t
#no_bounds_check {
if read_offset < len(src) {
offset = read_offset
r, w = rune(src[read_offset]), 1
if t.read_offset < len(t.src) {
t.offset = t.read_offset
t.r, t.w = rune(t.src[t.read_offset]), 1
switch {
case r == 0:
case t.r == 0:
return .Illegal_NUL_Character
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune_in_string(src[read_offset:])
if r == utf8.RUNE_ERROR && w == 1 {
case t.r >= utf8.RUNE_SELF:
t.r, t.w = utf8.decode_rune_in_string(t.src[t.read_offset:])
if t.r == utf8.RUNE_ERROR && t.w == 1 {
return .Illegal_UTF_Encoding
} else if r == utf8.RUNE_BOM && offset > 0 {
} else if t.r == utf8.RUNE_BOM && t.offset > 0 {
return .Illegal_BOM
}
}
read_offset += w
t.read_offset += t.w
return .None
} else {
offset = len(src)
r = -1
t.offset = len(t.src)
t.r = -1
return
}
}
@@ -273,26 +271,25 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
All of these would be in the ASCII range.
Even if one is not, it doesn't matter. All characters we need to compare to extract are.
*/
using t
length := len(t.src)
found := false
#no_bounds_check {
for read_offset < length {
if src[read_offset] == ';' {
for t.read_offset < length {
if t.src[t.read_offset] == ';' {
t.read_offset += 1
found = true
read_offset += 1
break
}
read_offset += 1
t.read_offset += 1
}
}
if found {
return string(src[offset + 1 : read_offset - 1]), .None
return string(t.src[t.offset + 1 : t.read_offset - 1]), .None
}
return string(src[offset : read_offset]), .Invalid_Entity_Encoding
return string(t.src[t.offset : t.read_offset]), .Invalid_Entity_Encoding
}
/*
+21 -23
View File
@@ -19,43 +19,39 @@ import "core:fmt"
*/
print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) {
if doc == nil { return }
using fmt
written += wprintf(writer, "[XML Prolog]\n")
written += fmt.wprintf(writer, "[XML Prolog]\n")
for attr in doc.prologue {
written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
written += fmt.wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
}
written += wprintf(writer, "[Encoding] %v\n", doc.encoding)
written += fmt.wprintf(writer, "[Encoding] %v\n", doc.encoding)
if len(doc.doctype.ident) > 0 {
written += wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
if len(doc.doctype.rest) > 0 {
wprintf(writer, "\t%v\n", doc.doctype.rest)
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
}
}
for comment in doc.comments {
written += wprintf(writer, "[Pre-root comment] %v\n", comment)
written += fmt.wprintf(writer, "[Pre-root comment] %v\n", comment)
}
if len(doc.elements) > 0 {
wprintln(writer, " --- ")
fmt.wprintln(writer, " --- ")
print_element(writer, doc, 0)
wprintln(writer, " --- ")
fmt.wprintln(writer, " --- ")
}
return written, .None
}
print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) {
using fmt
tab :: proc(writer: io.Writer, indent: int) {
for _ in 0..=indent {
wprintf(writer, "\t")
fmt.wprintf(writer, "\t")
}
}
@@ -64,22 +60,24 @@ print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID,
element := doc.elements[element_id]
if element.kind == .Element {
wprintf(writer, "<%v>\n", element.ident)
if len(element.value) > 0 {
tab(writer, indent + 1)
wprintf(writer, "[Value] %v\n", element.value)
fmt.wprintf(writer, "<%v>\n", element.ident)
for value in element.value {
switch v in value {
case string:
tab(writer, indent + 1)
fmt.wprintf(writer, "[Value] %v\n", v)
case Element_ID:
print_element(writer, doc, v, indent + 1)
}
}
for attr in element.attribs {
tab(writer, indent + 1)
wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
}
for child in element.children {
print_element(writer, doc, child, indent + 1)
fmt.wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
}
} else if element.kind == .Comment {
wprintf(writer, "[COMMENT] %v\n", element.value)
fmt.wprintf(writer, "[COMMENT] %v\n", element.value)
}
return written, .None
+3 -3
View File
@@ -72,10 +72,10 @@ example :: proc() {
return
}
printf("Found `<charlist>` with %v children, %v elements total\n", len(docs[0].elements[charlist].children), docs[0].element_count)
printf("Found `<charlist>` with %v children, %v elements total\n", len(docs[0].elements[charlist].value), docs[0].element_count)
crc32 := doc_hash(docs[0])
printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0xcaa042b9 else "🤬", crc32)
crc32 := doc_hash(docs[0], false)
printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0x420dbac5 else "🤬", crc32)
for round in 0..<N {
defer xml.destroy(docs[round])
+17 -12
View File
@@ -13,20 +13,25 @@ find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string
tag := doc.elements[parent_id]
count := 0
for child_id in tag.children {
child := doc.elements[child_id]
/*
Skip commments. They have no name.
*/
if child.kind != .Element { continue }
for v in tag.value {
switch child_id in v {
case string: continue
case Element_ID:
child := doc.elements[child_id]
/*
Skip commments. They have no name.
*/
if child.kind != .Element { continue }
/*
If the ident matches and it's the nth such child, return it.
*/
if child.ident == ident {
if count == nth { return child_id, true }
count += 1
/*
If the ident matches and it's the nth such child, return it.
*/
if child.ident == ident {
if count == nth { return child_id, true }
count += 1
}
}
}
return 0, false
}
+16 -16
View File
@@ -125,38 +125,38 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
}
@(optimization_mode="speed")
advance_rune :: proc(using t: ^Tokenizer) {
advance_rune :: proc(t: ^Tokenizer) {
#no_bounds_check {
/*
Already bounds-checked here.
*/
if read_offset < len(src) {
offset = read_offset
if ch == '\n' {
line_offset = offset
line_count += 1
if t.read_offset < len(t.src) {
t.offset = t.read_offset
if t.ch == '\n' {
t.line_offset = t.offset
t.line_count += 1
}
r, w := rune(src[read_offset]), 1
r, w := rune(t.src[t.read_offset]), 1
switch {
case r == 0:
error(t, t.offset, "illegal character NUL")
case r >= utf8.RUNE_SELF:
r, w = #force_inline utf8.decode_rune_in_string(src[read_offset:])
r, w = #force_inline utf8.decode_rune_in_string(t.src[t.read_offset:])
if r == utf8.RUNE_ERROR && w == 1 {
error(t, t.offset, "illegal UTF-8 encoding")
} else if r == utf8.RUNE_BOM && offset > 0 {
} else if r == utf8.RUNE_BOM && t.offset > 0 {
error(t, t.offset, "illegal byte order mark")
}
}
read_offset += w
ch = r
t.read_offset += w
t.ch = r
} else {
offset = len(src)
if ch == '\n' {
line_offset = offset
line_count += 1
t.offset = len(t.src)
if t.ch == '\n' {
t.line_offset = t.offset
t.line_count += 1
}
ch = -1
t.ch = -1
}
}
}
+15 -27
View File
@@ -125,16 +125,19 @@ Document :: struct {
Element :: struct {
ident: string,
value: string,
value: [dynamic]Value,
attribs: Attributes,
kind: enum {
Element = 0,
Comment,
},
parent: Element_ID,
children: [dynamic]Element_ID,
}
Value :: union {
string,
Element_ID,
}
Attribute :: struct {
@@ -247,9 +250,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
err = .Unexpected_Token
element, parent: Element_ID
tag_is_open := false
first_element := true
open: Token
/*
@@ -275,16 +275,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
e.g. <odin - Start of new element.
*/
element = new_element(doc)
tag_is_open = true
if first_element {
/*
First element.
*/
parent = element
first_element = false
if element == 0 { // First Element
parent = element
} else {
append(&doc.elements[parent].children, element)
append(&doc.elements[parent].value, element)
}
doc.elements[element].parent = parent
@@ -324,7 +318,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
expect(t, .Gt) or_return
parent = doc.elements[element].parent
element = parent
tag_is_open = false
case:
error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
@@ -344,7 +337,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
}
parent = doc.elements[element].parent
element = parent
tag_is_open = false
} else if open.kind == .Exclaim {
/*
@@ -392,8 +384,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
el := new_element(doc)
doc.elements[el].parent = element
doc.elements[el].kind = .Comment
doc.elements[el].value = comment
append(&doc.elements[element].children, el)
append(&doc.elements[el].value, comment)
append(&doc.elements[element].value, el)
}
}
@@ -436,9 +428,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
/*
End of file.
*/
if tag_is_open {
return doc, .Premature_EOF
}
break loop
case:
@@ -450,7 +439,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
needs_processing |= .Decode_SGML_Entities in opts.flags
if !needs_processing {
doc.elements[element].value = body_text
append(&doc.elements[element].value, body_text)
continue
}
@@ -472,10 +461,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
decoded, decode_err := entity.decode_xml(body_text, decode_opts)
if decode_err == .None {
doc.elements[element].value = decoded
append(&doc.elements[element].value, decoded)
append(&doc.strings_to_free, decoded)
} else {
doc.elements[element].value = body_text
append(&doc.elements[element].value, body_text)
}
}
}
@@ -518,7 +507,7 @@ destroy :: proc(doc: ^Document) {
for el in doc.elements {
delete(el.attribs)
delete(el.children)
delete(el.value)
}
delete(doc.elements)
@@ -710,6 +699,5 @@ new_element :: proc(doc: ^Document) -> (id: Element_ID) {
cur := doc.element_count
doc.element_count += 1
return cur
}
+11 -11
View File
@@ -835,22 +835,22 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
// - fi: A pointer to an Info structure
// - verb: The invalid format verb
//
fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
prev_in_bad := fi.in_bad
defer fi.in_bad = prev_in_bad
fi.in_bad = true
io.write_string(writer, "%!", &fi.n)
io.write_rune(writer, verb, &fi.n)
io.write_byte(writer, '(', &fi.n)
if arg.id != nil {
reflect.write_typeid(writer, arg.id, &fi.n)
io.write_byte(writer, '=', &fi.n)
fmt_value(fi, arg, 'v')
io.write_string(fi.writer, "%!", &fi.n)
io.write_rune(fi.writer, verb, &fi.n)
io.write_byte(fi.writer, '(', &fi.n)
if fi.arg.id != nil {
reflect.write_typeid(fi.writer, fi.arg.id, &fi.n)
io.write_byte(fi.writer, '=', &fi.n)
fmt_value(fi, fi.arg, 'v')
} else {
io.write_string(writer, "<nil>", &fi.n)
io.write_string(fi.writer, "<nil>", &fi.n)
}
io.write_byte(writer, ')', &fi.n)
io.write_byte(fi.writer, ')', &fi.n)
}
// Formats a boolean value according to the specified format verb
//
@@ -859,7 +859,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
// - b: The boolean value to format
// - verb: The format verb
//
fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) {
fmt_bool :: proc(fi: ^Info, b: bool, verb: rune) {
switch verb {
case 't', 'v':
fmt_string(fi, b ? "true" : "false", 's')
+8 -7
View File
@@ -4,13 +4,14 @@ import "core:bytes"
import "core:image"
destroy :: proc(img: ^image.Image) -> bool {
if img == nil do return false
if img == nil {
return false
}
defer free(img)
bytes.buffer_destroy(&img.pixels)
info, ok := img.metadata.(^image.Netpbm_Info)
if !ok do return false
info := img.metadata.(^image.Netpbm_Info) or_return
header_destroy(&info.header)
free(info)
@@ -19,9 +20,9 @@ destroy :: proc(img: ^image.Image) -> bool {
return true
}
header_destroy :: proc(using header: ^Header) {
if format == .P7 && tupltype != "" {
delete(tupltype)
tupltype = ""
header_destroy :: proc(header: ^Header) {
if header.format == .P7 && header.tupltype != "" {
delete(header.tupltype)
header.tupltype = ""
}
}
+1
View File
@@ -1,3 +1,4 @@
//+vet !using-stmt
package netpbm
import "core:bytes"
+3 -4
View File
@@ -80,11 +80,10 @@ time :: proc(c: image.PNG_Chunk) -> (res: tIME, ok: bool) {
}
core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
if png_time, png_ok := time(c); png_ok {
using png_time
if t, png_ok := time(c); png_ok {
return coretime.datetime_to_time(
int(year), int(month), int(day),
int(hour), int(minute), int(second),
int(t.year), int(t.month), int(t.day),
int(t.hour), int(t.minute), int(t.second),
)
} else {
return {}, false
+8 -8
View File
@@ -11,6 +11,7 @@
// package png implements a PNG image reader
//
// The PNG specification is at https://www.w3.org/TR/PNG/.
//+vet !using-stmt
package png
import "core:compress"
@@ -444,15 +445,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
img.width = int(header.width)
img.height = int(header.height)
using header
h := image.PNG_IHDR{
width = width,
height = height,
bit_depth = bit_depth,
color_type = color_type,
compression_method = compression_method,
filter_method = filter_method,
interlace_method = interlace_method,
width = header.width,
height = header.height,
bit_depth = header.bit_depth,
color_type = header.color_type,
compression_method = header.compression_method,
filter_method = header.filter_method,
interlace_method = header.interlace_method,
}
info.header = h
+12 -12
View File
@@ -2286,20 +2286,20 @@ F64_MASK :: 0x7ff
F64_SHIFT :: 64 - 12
F64_BIAS :: 0x3ff
INF_F16 :f16: 0h7C00
NEG_INF_F16 :f16: 0hFC00
INF_F16 :: f16(0h7C00)
NEG_INF_F16 :: f16(0hFC00)
SNAN_F16 :f16: 0h7C01
QNAN_F16 :f16: 0h7E01
SNAN_F16 :: f16(0h7C01)
QNAN_F16 :: f16(0h7E01)
INF_F32 :f32: 0h7F80_0000
NEG_INF_F32 :f32: 0hFF80_0000
INF_F32 :: f32(0h7F80_0000)
NEG_INF_F32 :: f32(0hFF80_0000)
SNAN_F32 :f32: 0hFF80_0001
QNAN_F32 :f32: 0hFFC0_0001
SNAN_F32 :: f32(0hFF80_0001)
QNAN_F32 :: f32(0hFFC0_0001)
INF_F64 :f64: 0h7FF0_0000_0000_0000
NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
INF_F64 :: f64(0h7FF0_0000_0000_0000)
NEG_INF_F64 :: f64(0hFFF0_0000_0000_0000)
SNAN_F64 :f64: 0h7FF0_0000_0000_0001
QNAN_F64 :f64: 0h7FF8_0000_0000_0001
SNAN_F64 :: f64(0h7FF0_0000_0000_0001)
QNAN_F64 :: f64(0h7FF8_0000_0000_0001)
+52 -52
View File
@@ -111,11 +111,11 @@ begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
return tmp
}
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
assert(arena.offset >= prev_offset)
assert(arena.temp_count > 0)
arena.offset = prev_offset
arena.temp_count -= 1
end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) {
assert(tmp.arena.offset >= tmp.prev_offset)
assert(tmp.arena.temp_count > 0)
tmp.arena.offset = tmp.prev_offset
tmp.arena.temp_count -= 1
}
@@ -702,11 +702,11 @@ dynamic_pool_init :: proc(pool: ^Dynamic_Pool,
pool. used_blocks.allocator = array_allocator
}
dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) {
dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) {
dynamic_pool_free_all(pool)
delete(unused_blocks)
delete(used_blocks)
delete(out_band_allocations)
delete(pool.unused_blocks)
delete(pool.used_blocks)
delete(pool.out_band_allocations)
zero(pool, size_of(pool^))
}
@@ -719,90 +719,90 @@ dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, Allocato
}
@(require_results)
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
if block_allocator.procedure == nil {
dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
cycle_new_block :: proc(p: ^Dynamic_Pool) -> (err: Allocator_Error) {
if p.block_allocator.procedure == nil {
panic("You must call pool_init on a Pool before using it")
}
if current_block != nil {
append(&used_blocks, current_block)
if p.current_block != nil {
append(&p.used_blocks, p.current_block)
}
new_block: rawptr
if len(unused_blocks) > 0 {
new_block = pop(&unused_blocks)
if len(p.unused_blocks) > 0 {
new_block = pop(&p.unused_blocks)
} else {
data: []byte
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0)
data, err = p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
p.block_size, p.alignment,
nil, 0)
new_block = raw_data(data)
}
bytes_left = block_size
current_pos = new_block
current_block = new_block
p.bytes_left = p.block_size
p.current_pos = new_block
p.current_block = new_block
return
}
n := bytes
extra := alignment - (n % alignment)
extra := p.alignment - (n % p.alignment)
n += extra
if n >= out_band_size {
assert(block_allocator.procedure != nil)
memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment,
nil, 0)
if n >= p.out_band_size {
assert(p.block_allocator.procedure != nil)
memory, err := p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
p.block_size, p.alignment,
nil, 0)
if memory != nil {
append(&out_band_allocations, raw_data(memory))
append(&p.out_band_allocations, raw_data(memory))
}
return memory, err
}
if bytes_left < n {
err := cycle_new_block(pool)
if p.bytes_left < n {
err := cycle_new_block(p)
if err != nil {
return nil, err
}
if current_block == nil {
if p.current_block == nil {
return nil, .Out_Of_Memory
}
}
memory := current_pos
current_pos = ptr_offset((^byte)(current_pos), n)
bytes_left -= n
return byte_slice(memory, bytes), nil
memory := p.current_pos
p.current_pos = ([^]byte)(p.current_pos)[n:]
p.bytes_left -= n
return ([^]byte)(memory)[:bytes], nil
}
dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) {
if current_block != nil {
append(&unused_blocks, current_block)
current_block = nil
dynamic_pool_reset :: proc(p: ^Dynamic_Pool) {
if p.current_block != nil {
append(&p.unused_blocks, p.current_block)
p.current_block = nil
}
for block in used_blocks {
append(&unused_blocks, block)
for block in p.used_blocks {
append(&p.unused_blocks, block)
}
clear(&used_blocks)
clear(&p.used_blocks)
for a in out_band_allocations {
free(a, block_allocator)
for a in p.out_band_allocations {
free(a, p.block_allocator)
}
clear(&out_band_allocations)
clear(&p.out_band_allocations)
bytes_left = 0 // Make new allocations call `cycle_new_block` again.
p.bytes_left = 0 // Make new allocations call `cycle_new_block` again.
}
dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) {
dynamic_pool_reset(pool)
dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) {
dynamic_pool_reset(p)
for block in unused_blocks {
free(block, block_allocator)
for block in p.unused_blocks {
free(block, p.block_allocator)
}
clear(&unused_blocks)
clear(&p.unused_blocks)
}
+35 -35
View File
@@ -63,100 +63,100 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host,
}
join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string {
using strings
b := strings.builder_make(allocator)
strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
b := builder_make(allocator)
builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
write_string(&b, scheme)
write_string(&b, "://")
write_string(&b, trim_space(host))
strings.write_string(&b, scheme)
strings.write_string(&b, "://")
strings.write_string(&b, strings.trim_space(host))
if path != "" {
if path[0] != '/' do write_string(&b, "/")
write_string(&b, trim_space(path))
if path[0] != '/' {
strings.write_string(&b, "/")
}
strings.write_string(&b, strings.trim_space(path))
}
query_length := len(queries)
if query_length > 0 do write_string(&b, "?")
if query_length > 0 {
strings.write_string(&b, "?")
}
i := 0
for query_name, query_value in queries {
write_string(&b, query_name)
strings.write_string(&b, query_name)
if query_value != "" {
write_string(&b, "=")
write_string(&b, query_value)
strings.write_string(&b, "=")
strings.write_string(&b, query_value)
}
if i < query_length - 1 {
write_string(&b, "&")
strings.write_string(&b, "&")
}
i += 1
}
return to_string(b)
return strings.to_string(b)
}
percent_encode :: proc(s: string, allocator := context.allocator) -> string {
using strings
b := builder_make(allocator)
builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
b := strings.builder_make(allocator)
strings.builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
for ch in s {
switch ch {
case 'A'..='Z', 'a'..='z', '0'..='9', '-', '_', '.', '~':
write_rune(&b, ch)
strings.write_rune(&b, ch)
case:
bytes, n := utf8.encode_rune(ch)
for byte in bytes[:n] {
buf: [2]u8 = ---
t := strconv.append_int(buf[:], i64(byte), 16)
write_rune(&b, '%')
write_string(&b, t)
strings.write_rune(&b, '%')
strings.write_string(&b, t)
}
}
}
return to_string(b)
return strings.to_string(b)
}
percent_decode :: proc(encoded_string: string, allocator := context.allocator) -> (decoded_string: string, ok: bool) {
using strings
b := builder_make(allocator)
builder_grow(&b, len(encoded_string))
defer if !ok do builder_destroy(&b)
b := strings.builder_make(allocator)
strings.builder_grow(&b, len(encoded_string))
defer if !ok do strings.builder_destroy(&b)
s := encoded_string
for len(s) > 0 {
i := index_byte(s, '%')
i := strings.index_byte(s, '%')
if i == -1 {
write_string(&b, s) // no '%'s; the string is already decoded
strings.write_string(&b, s) // no '%'s; the string is already decoded
break
}
write_string(&b, s[:i])
strings.write_string(&b, s[:i])
s = s[i:]
if len(s) == 0 do return // percent without anything after it
s = s[1:]
if s[0] == '%' {
write_byte(&b, '%')
strings.write_byte(&b, '%')
s = s[1:]
continue
}
if len(s) < 2 do return // percent without encoded value
if len(s) < 2 {
return // percent without encoded value
}
val := hex.decode_sequence(s[:2]) or_return
write_byte(&b, val)
strings.write_byte(&b, val)
s = s[2:]
}
ok = true
decoded_string = to_string(b)
decoded_string = strings.to_string(b)
return
}
+86 -92
View File
@@ -336,22 +336,20 @@ hint_current_line :: proc(p: ^Printer, hint: Line_Type) {
@(private)
visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
using ast
if decl == nil {
return
}
#partial switch v in decl.derived_stmt {
case ^Expr_Stmt:
case ^ast.Expr_Stmt:
move_line(p, decl.pos)
visit_expr(p, v.expr)
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
case ^When_Stmt:
visit_stmt(p, cast(^Stmt)decl)
case ^Foreign_Import_Decl:
case ^ast.When_Stmt:
visit_stmt(p, cast(^ast.Stmt)decl)
case ^ast.Foreign_Import_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -370,7 +368,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
for path in v.fullpaths {
push_ident_token(p, path, 0)
}
case ^Foreign_Block_Decl:
case ^ast.Foreign_Block_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -383,7 +381,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
visit_expr(p, v.foreign_library)
visit_stmt(p, v.body)
case ^Import_Decl:
case ^ast.Import_Decl:
move_line(p, decl.pos)
if v.name.text != "" {
@@ -395,7 +393,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
push_ident_token(p, v.fullpath, 1)
}
case ^Value_Decl:
case ^ast.Value_Decl:
if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos)
@@ -447,9 +445,9 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
for value in v.values {
#partial switch a in value.derived {
case ^Union_Type, ^Enum_Type, ^Struct_Type:
case ^ast.Union_Type, ^ast.Enum_Type, ^ast.Struct_Type:
add_semicolon = false || called_in_stmt
case ^Proc_Lit:
case ^ast.Proc_Lit:
add_semicolon = false
}
}
@@ -510,40 +508,38 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) {
@(private)
visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) {
using ast
if stmt == nil {
return
}
switch v in stmt.derived_stmt {
case ^Bad_Stmt:
case ^Bad_Decl:
case ^Package_Decl:
case ^ast.Bad_Stmt:
case ^ast.Bad_Decl:
case ^ast.Package_Decl:
case ^Empty_Stmt:
case ^ast.Empty_Stmt:
push_generic_token(p, .Semicolon, 0)
case ^Tag_Stmt:
case ^ast.Tag_Stmt:
push_generic_token(p, .Hash, 1)
push_generic_token(p, v.op.kind, 1, v.op.text)
visit_stmt(p, v.stmt)
case ^Import_Decl:
visit_decl(p, cast(^Decl)stmt, true)
case ^ast.Import_Decl:
visit_decl(p, cast(^ast.Decl)stmt, true)
return
case ^Value_Decl:
visit_decl(p, cast(^Decl)stmt, true)
case ^ast.Value_Decl:
visit_decl(p, cast(^ast.Decl)stmt, true)
return
case ^Foreign_Import_Decl:
visit_decl(p, cast(^Decl)stmt, true)
case ^ast.Foreign_Import_Decl:
visit_decl(p, cast(^ast.Decl)stmt, true)
return
case ^Foreign_Block_Decl:
visit_decl(p, cast(^Decl)stmt, true)
case ^ast.Foreign_Block_Decl:
visit_decl(p, cast(^ast.Decl)stmt, true)
return
case ^Using_Stmt:
case ^ast.Using_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Using, 1)
@@ -553,7 +549,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
case ^Block_Stmt:
case ^ast.Block_Stmt:
move_line(p, v.pos)
if v.pos.line == v.end.line {
@@ -583,7 +579,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_end_brace(p, v.end)
}
}
case ^If_Stmt:
case ^ast.If_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -606,7 +602,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
uses_do := false
if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do {
if check_stmt, ok := v.body.derived.(^ast.Block_Stmt); ok && check_stmt.uses_do {
uses_do = true
}
@@ -637,7 +633,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt)
}
case ^Switch_Stmt:
case ^ast.Switch_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -665,7 +661,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.cond)
visit_stmt(p, v.body)
case ^Case_Clause:
case ^ast.Case_Clause:
move_line(p, v.pos)
if !p.config.indent_cases {
@@ -689,7 +685,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if !p.config.indent_cases {
indent(p)
}
case ^Type_Switch_Stmt:
case ^ast.Type_Switch_Stmt:
move_line(p, v.pos)
hint_current_line(p, {.Switch_Stmt})
@@ -707,7 +703,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.tag)
visit_stmt(p, v.body)
case ^Assign_Stmt:
case ^ast.Assign_Stmt:
move_line(p, v.pos)
hint_current_line(p, {.Assign})
@@ -721,13 +717,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
case ^Expr_Stmt:
case ^ast.Expr_Stmt:
move_line(p, v.pos)
visit_expr(p, v.expr)
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
case ^For_Stmt:
case ^ast.For_Stmt:
// this should be simplified
move_line(p, v.pos)
@@ -764,7 +760,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.body)
case ^Inline_Range_Stmt:
case ^ast.Inline_Range_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -790,7 +786,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr)
visit_stmt(p, v.body)
case ^Range_Stmt:
case ^ast.Range_Stmt:
move_line(p, v.pos)
if v.label != nil {
@@ -816,7 +812,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr)
visit_stmt(p, v.body)
case ^Return_Stmt:
case ^ast.Return_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Return, 1)
@@ -828,7 +824,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
case ^Defer_Stmt:
case ^ast.Defer_Stmt:
move_line(p, v.pos)
push_generic_token(p, .Defer, 0)
@@ -837,7 +833,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons {
push_generic_token(p, .Semicolon, 0)
}
case ^When_Stmt:
case ^ast.When_Stmt:
move_line(p, v.pos)
push_generic_token(p, .When, 1)
visit_expr(p, v.cond)
@@ -857,7 +853,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt)
}
case ^Branch_Stmt:
case ^ast.Branch_Stmt:
move_line(p, v.pos)
push_generic_token(p, v.tok.kind, 0)
@@ -921,8 +917,6 @@ push_poly_params :: proc(p: ^Printer, poly_params: ^ast.Field_List) {
@(private)
visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
using ast
if expr == nil {
return
}
@@ -930,14 +924,14 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
set_source_position(p, expr.pos)
switch v in expr.derived_expr {
case ^Bad_Expr:
case ^ast.Bad_Expr:
case ^Tag_Expr:
case ^ast.Tag_Expr:
push_generic_token(p, .Hash, 1)
push_generic_token(p, v.op.kind, 1, v.op.text)
visit_expr(p, v.expr)
case ^Inline_Asm_Expr:
case ^ast.Inline_Asm_Expr:
push_generic_token(p, v.tok.kind, 1, v.tok.text)
push_generic_token(p, .Open_Paren, 1)
@@ -954,42 +948,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Comma, 0)
visit_expr(p, v.constraints_string)
push_generic_token(p, .Close_Brace, 0)
case ^Undef:
case ^ast.Undef:
push_generic_token(p, .Undef, 1)
case ^Auto_Cast:
case ^ast.Auto_Cast:
push_generic_token(p, v.op.kind, 1)
visit_expr(p, v.expr)
case ^Ternary_If_Expr:
case ^ast.Ternary_If_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y)
case ^Ternary_When_Expr:
case ^ast.Ternary_When_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y)
case ^Or_Else_Expr:
case ^ast.Or_Else_Expr:
visit_expr(p, v.x)
push_generic_token(p, v.token.kind, 1)
visit_expr(p, v.y)
case ^Or_Return_Expr:
case ^ast.Or_Return_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.token.kind, 1)
case ^Selector_Call_Expr:
case ^ast.Selector_Call_Expr:
visit_expr(p, v.call.expr)
push_generic_token(p, .Open_Paren, 1)
visit_exprs(p, v.call.args, {.Add_Comma})
push_generic_token(p, .Close_Paren, 0)
case ^Ellipsis:
case ^ast.Ellipsis:
push_generic_token(p, .Ellipsis, 1)
visit_expr(p, v.expr)
case ^Relative_Type:
case ^ast.Relative_Type:
visit_expr(p, v.tag)
visit_expr(p, v.type)
case ^Slice_Expr:
case ^ast.Slice_Expr:
visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.low)
@@ -999,37 +993,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_expr(p, v.high)
}
push_generic_token(p, .Close_Bracket, 0)
case ^Ident:
case ^ast.Ident:
if .Enforce_Poly_Names in options {
push_generic_token(p, .Dollar, 1)
push_ident_token(p, v.name, 0)
} else {
push_ident_token(p, v.name, 1)
}
case ^Deref_Expr:
case ^ast.Deref_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0)
case ^Type_Cast:
case ^ast.Type_Cast:
push_generic_token(p, v.tok.kind, 1)
push_generic_token(p, .Open_Paren, 0)
visit_expr(p, v.type)
push_generic_token(p, .Close_Paren, 0)
merge_next_token(p)
visit_expr(p, v.expr)
case ^Basic_Directive:
case ^ast.Basic_Directive:
push_generic_token(p, v.tok.kind, 1)
push_ident_token(p, v.name, 0)
case ^Distinct_Type:
case ^ast.Distinct_Type:
push_generic_token(p, .Distinct, 1)
visit_expr(p, v.type)
case ^Dynamic_Array_Type:
case ^ast.Dynamic_Array_Type:
visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1)
push_generic_token(p, .Dynamic, 0)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.elem)
case ^Bit_Set_Type:
case ^ast.Bit_Set_Type:
push_generic_token(p, .Bit_Set, 1)
push_generic_token(p, .Open_Bracket, 0)
@@ -1041,7 +1035,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
push_generic_token(p, .Close_Bracket, 0)
case ^Union_Type:
case ^ast.Union_Type:
push_generic_token(p, .Union, 1)
push_poly_params(p, v.poly_params)
@@ -1066,7 +1060,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
visit_end_brace(p, v.end)
}
case ^Enum_Type:
case ^ast.Enum_Type:
push_generic_token(p, .Enum, 1)
hint_current_line(p, {.Enum})
@@ -1089,7 +1083,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
set_source_position(p, v.end)
case ^Struct_Type:
case ^ast.Struct_Type:
push_generic_token(p, .Struct, 1)
hint_current_line(p, {.Struct})
@@ -1124,7 +1118,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
}
set_source_position(p, v.end)
case ^Proc_Lit:
case ^ast.Proc_Lit:
switch v.inlining {
case .None:
case .Inline:
@@ -1143,16 +1137,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
} else {
push_generic_token(p, .Undef, 1)
}
case ^Proc_Type:
case ^ast.Proc_Type:
visit_proc_type(p, v)
case ^Basic_Lit:
case ^ast.Basic_Lit:
push_generic_token(p, v.tok.kind, 1, v.tok.text)
case ^Binary_Expr:
case ^ast.Binary_Expr:
visit_binary_expr(p, v)
case ^Implicit_Selector_Expr:
case ^ast.Implicit_Selector_Expr:
push_generic_token(p, .Period, 1)
push_ident_token(p, v.field.name, 0)
case ^Call_Expr:
case ^ast.Call_Expr:
visit_expr(p, v.expr)
push_format_token(p,
@@ -1167,34 +1161,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
push_generic_token(p, .Close_Paren, 0)
case ^Typeid_Type:
case ^ast.Typeid_Type:
push_generic_token(p, .Typeid, 1)
if v.specialization != nil {
push_generic_token(p, .Quo, 0)
visit_expr(p, v.specialization)
}
case ^Selector_Expr:
case ^ast.Selector_Expr:
visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0)
visit_expr(p, v.field)
case ^Paren_Expr:
case ^ast.Paren_Expr:
push_generic_token(p, .Open_Paren, 1)
visit_expr(p, v.expr)
push_generic_token(p, .Close_Paren, 0)
case ^Index_Expr:
case ^ast.Index_Expr:
visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.index)
push_generic_token(p, .Close_Bracket, 0)
case ^Matrix_Index_Expr:
case ^ast.Matrix_Index_Expr:
visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.row_index)
push_generic_token(p, .Comma, 0)
visit_expr(p, v.column_index)
push_generic_token(p, .Close_Bracket, 0)
case ^Proc_Group:
case ^ast.Proc_Group:
push_generic_token(p, v.tok.kind, 1)
if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
@@ -1209,7 +1203,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0)
}
case ^Comp_Lit:
case ^ast.Comp_Lit:
if v.type != nil {
visit_expr(p, v.type)
}
@@ -1226,18 +1220,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0)
}
case ^Unary_Expr:
case ^ast.Unary_Expr:
push_generic_token(p, v.op.kind, 1)
merge_next_token(p)
visit_expr(p, v.expr)
case ^Field_Value:
case ^ast.Field_Value:
visit_expr(p, v.field)
push_generic_token(p, .Eq, 1)
visit_expr(p, v.value)
case ^Type_Assertion:
case ^ast.Type_Assertion:
visit_expr(p, v.expr)
if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" {
if unary, ok := v.type.derived.(^ast.Unary_Expr); ok && unary.op.text == "?" {
push_generic_token(p, .Period, 0)
visit_expr(p, v.type)
} else {
@@ -1247,13 +1241,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Paren, 0)
}
case ^Pointer_Type:
case ^ast.Pointer_Type:
push_generic_token(p, .Pointer, 1)
merge_next_token(p)
visit_expr(p, v.elem)
case ^Implicit:
case ^ast.Implicit:
push_generic_token(p, v.tok.kind, 1)
case ^Poly_Type:
case ^ast.Poly_Type:
push_generic_token(p, .Dollar, 1)
merge_next_token(p)
visit_expr(p, v.type)
@@ -1263,28 +1257,28 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
merge_next_token(p)
visit_expr(p, v.specialization)
}
case ^Array_Type:
case ^ast.Array_Type:
visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1)
visit_expr(p, v.len)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.elem)
case ^Map_Type:
case ^ast.Map_Type:
push_generic_token(p, .Map, 1)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.key)
push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p)
visit_expr(p, v.value)
case ^Helper_Type:
case ^ast.Helper_Type:
visit_expr(p, v.type)
case ^Multi_Pointer_Type:
case ^ast.Multi_Pointer_Type:
push_generic_token(p, .Open_Bracket, 1)
push_generic_token(p, .Pointer, 0)
push_generic_token(p, .Close_Bracket, 0)
visit_expr(p, v.elem)
case ^Matrix_Type:
case ^ast.Matrix_Type:
push_generic_token(p, .Matrix, 1)
push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.row_count)
+16 -16
View File
@@ -75,34 +75,34 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
t.error_count += 1
}
advance_rune :: proc(using t: ^Tokenizer) {
if read_offset < len(src) {
offset = read_offset
if ch == '\n' {
line_offset = offset
line_count += 1
advance_rune :: proc(t: ^Tokenizer) {
if t.read_offset < len(t.src) {
t.offset = t.read_offset
if t.ch == '\n' {
t.line_offset = t.offset
t.line_count += 1
}
r, w := rune(src[read_offset]), 1
r, w := rune(t.src[t.read_offset]), 1
switch {
case r == 0:
error(t, t.offset, "illegal character NUL")
case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune_in_string(src[read_offset:])
r, w = utf8.decode_rune_in_string(t.src[t.read_offset:])
if r == utf8.RUNE_ERROR && w == 1 {
error(t, t.offset, "illegal UTF-8 encoding")
} else if r == utf8.RUNE_BOM && offset > 0 {
} else if r == utf8.RUNE_BOM && t.offset > 0 {
error(t, t.offset, "illegal byte order mark")
}
}
read_offset += w
ch = r
t.read_offset += w
t.ch = r
} else {
offset = len(src)
if ch == '\n' {
line_offset = offset
line_count += 1
t.offset = len(t.src)
if t.ch == '\n' {
t.line_offset = t.offset
t.line_count += 1
}
ch = -1
t.ch = -1
}
}
+118 -98
View File
@@ -414,68 +414,21 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
tk := map_cell_index_dynamic(sk, info.ks, 1)
tv := map_cell_index_dynamic(sv, info.vs, 1)
for {
hp := &hs[pos]
element_hash := hp^
swap_loop: for {
element_hash := hs[pos]
if map_hash_is_empty(element_hash) {
kp := map_cell_index_dynamic(ks, info.ks, pos)
vp := map_cell_index_dynamic(vs, info.vs, pos)
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
hp^ = h
k_dst := map_cell_index_dynamic(ks, info.ks, pos)
v_dst := map_cell_index_dynamic(vs, info.vs, pos)
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
return result if result != 0 else vp
return result if result != 0 else v_dst
}
if map_hash_is_deleted(element_hash) {
next_pos := (pos + 1) & mask
// backward shift
for !map_hash_is_empty(hs[next_pos]) {
probe_distance := map_probe_distance(m^, hs[next_pos], next_pos)
if probe_distance == 0 {
break
}
probe_distance -= 1
kp := map_cell_index_dynamic(ks, info.ks, pos)
vp := map_cell_index_dynamic(vs, info.vs, pos)
kn := map_cell_index_dynamic(ks, info.ks, next_pos)
vn := map_cell_index_dynamic(vs, info.vs, next_pos)
if distance > probe_distance {
if result == 0 {
result = vp
}
// move stored into pos; store next
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
hs[pos] = h
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kn), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(vn), size_of_v)
h = hs[next_pos]
} else {
// move next back 1
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(kn), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(vn), size_of_v)
hs[pos] = hs[next_pos]
distance = probe_distance
}
hs[next_pos] = 0
pos = (pos + 1) & mask
next_pos = (next_pos + 1) & mask
distance += 1
}
kp := map_cell_index_dynamic(ks, info.ks, pos)
vp := map_cell_index_dynamic(vs, info.vs, pos)
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
hs[pos] = h
return result if result != 0 else vp
break swap_loop
}
if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
@@ -495,8 +448,8 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(tv), size_of_v)
th := h
h = hp^
hp^ = th
h = hs[pos]
hs[pos] = th
distance = probe_distance
}
@@ -504,6 +457,103 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
pos = (pos + 1) & mask
distance += 1
}
// backward shift loop
hs[pos] = 0
look_ahead: uintptr = 1
for {
la_pos := (pos + look_ahead) & mask
element_hash := hs[la_pos]
if map_hash_is_deleted(element_hash) {
look_ahead += 1
hs[la_pos] = 0
continue
}
k_dst := map_cell_index_dynamic(ks, info.ks, pos)
v_dst := map_cell_index_dynamic(vs, info.vs, pos)
if map_hash_is_empty(element_hash) {
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
return result if result != 0 else v_dst
}
k_src := map_cell_index_dynamic(ks, info.ks, la_pos)
v_src := map_cell_index_dynamic(vs, info.vs, la_pos)
probe_distance := map_probe_distance(m^, element_hash, la_pos)
if probe_distance < look_ahead {
// probed can be made ideal while placing saved (ending condition)
if result == 0 {
result = v_dst
}
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
// This will be an ideal move
pos = (la_pos - probe_distance) & mask
look_ahead -= probe_distance
// shift until we hit ideal/empty
for probe_distance != 0 {
k_dst = map_cell_index_dynamic(ks, info.ks, pos)
v_dst = map_cell_index_dynamic(vs, info.vs, pos)
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v)
hs[pos] = element_hash
hs[la_pos] = 0
pos = (pos + 1) & mask
la_pos = (la_pos + 1) & mask
look_ahead = (la_pos - pos) & mask
element_hash = hs[la_pos]
if map_hash_is_empty(element_hash) {
return
}
probe_distance = map_probe_distance(m^, element_hash, la_pos)
if probe_distance == 0 {
return
}
// can be ideal?
if probe_distance < look_ahead {
pos = (la_pos - probe_distance) & mask
}
k_src = map_cell_index_dynamic(ks, info.ks, la_pos)
v_src = map_cell_index_dynamic(vs, info.vs, la_pos)
}
return
} else if distance < probe_distance - look_ahead {
// shift back probed
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v)
hs[pos] = element_hash
hs[la_pos] = 0
} else {
// place saved, save probed
if result == 0 {
result = v_dst
}
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(k_src), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(v_src), size_of_v)
h = hs[la_pos]
hs[la_pos] = 0
distance = probe_distance - look_ahead
}
pos = (pos + 1) & mask
distance += 1
}
}
@(require_results)
@@ -696,49 +746,19 @@ map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n
m.len -= 1
ok = true
{ // coalesce tombstones
// HACK NOTE(bill): This is an ugly bodge but it is coalescing the tombstone slots
mask := (uintptr(1)<<map_log2_cap(m^)) - 1
curr_index := uintptr(index)
// TODO(bill): determine a good value for this empirically
// if we do not implement backward shift deletion
PROBE_COUNT :: 8
for _ in 0..<PROBE_COUNT {
next_index := (curr_index + 1) & mask
if next_index == index {
// looped around
break
}
// if the next element is empty or has zero probe distance, then any lookup
// will always fail on the next, so we can clear both of them
hash := hs[next_index]
if map_hash_is_empty(hash) || map_probe_distance(m^, hash, next_index) == 0 {
hs[curr_index] = 0
return
}
// now the next element will have a probe count of at least one,
// so it can use the delete slot instead
hs[curr_index] = hs[next_index]
mem_copy_non_overlapping(
rawptr(map_cell_index_dynamic(ks, info.ks, curr_index)),
rawptr(map_cell_index_dynamic(ks, info.ks, next_index)),
int(info.ks.size_of_type),
)
mem_copy_non_overlapping(
rawptr(map_cell_index_dynamic(vs, info.vs, curr_index)),
rawptr(map_cell_index_dynamic(vs, info.vs, next_index)),
int(info.vs.size_of_type),
)
curr_index = next_index
}
mask := (uintptr(1)<<map_log2_cap(m^)) - 1
curr_index := uintptr(index)
next_index := (curr_index + 1) & mask
// if the next element is empty or has zero probe distance, then any lookup
// will always fail on the next, so we can clear both of them
hash := hs[next_index]
if map_hash_is_empty(hash) || map_probe_distance(m^, hash, next_index) == 0 {
hs[curr_index] = 0
} else {
hs[curr_index] |= TOMBSTONE_MASK
}
return
}
+2
View File
@@ -71,6 +71,8 @@ Error :: enum {
TS_File_Expected_Source,
TS_File_Expected_Translation,
TS_File_Expected_NumerusForm,
Bad_Str,
Bad_Id,
}
+34 -8
View File
@@ -30,10 +30,26 @@ TS_XML_Options := xml.Options{
parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
context.allocator = allocator
get_str :: proc(val: xml.Value) -> (str: string, err: Error) {
v, ok := val.(string)
if ok {
return v, .None
}
return "", .Bad_Str
}
get_id :: proc(val: xml.Value) -> (str: xml.Element_ID, err: Error) {
v, ok := val.(xml.Element_ID)
if ok {
return v, .None
}
return 0, .Bad_Id
}
ts, xml_err := xml.parse(data, TS_XML_Options)
defer xml.destroy(ts)
if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].children) == 0 {
if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].value) == 0 {
return nil, .TS_File_Parse_Error
}
@@ -46,10 +62,12 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
section: ^Section
for child_id in ts.elements[0].children {
for value in ts.elements[0].value {
child_id := get_id(value) or_return
// These should be <context>s.
child := ts.elements[child_id]
if child.ident != "context" {
if ts.elements[child_id].ident != "context" {
return translation, .TS_File_Expected_Context
}
@@ -61,7 +79,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
section_name, _ := strings.intern_get(&translation.intern, "")
if !options.merge_sections {
section_name, _ = strings.intern_get(&translation.intern, ts.elements[section_name_id].value)
value_text := get_str(ts.elements[section_name_id].value[0]) or_return
section_name, _ = strings.intern_get(&translation.intern, value_text)
}
if section_name not_in translation.k_v {
@@ -92,8 +111,14 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
return translation, .TS_File_Expected_Translation
}
source, _ := strings.intern_get(&translation.intern, ts.elements[source_id].value)
xlat, _ := strings.intern_get(&translation.intern, ts.elements[translation_id].value)
source := get_str(ts.elements[source_id].value[0]) or_return
source, _ = strings.intern_get(&translation.intern, source)
xlat := ""
if !has_plurals {
xlat = get_str(ts.elements[translation_id].value[0]) or_return
xlat, _ = strings.intern_get(&translation.intern, xlat)
}
if source in section {
return translation, .Duplicate_Key
@@ -124,7 +149,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
if !numerus_found {
break
}
numerus, _ := strings.intern_get(&translation.intern, ts.elements[numerus_id].value)
numerus := get_str(ts.elements[numerus_id].value[0]) or_return
numerus, _ = strings.intern_get(&translation.intern, numerus)
section[source][num_plurals] = numerus
num_plurals += 1
+2 -2
View File
@@ -129,8 +129,8 @@ _destroy :: proc(thread: ^Thread) {
free(thread, thread.creation_allocator)
}
_terminate :: proc(using thread : ^Thread, exit_code: int) {
win32.TerminateThread(win32_thread, u32(exit_code))
_terminate :: proc(thread: ^Thread, exit_code: int) {
win32.TerminateThread(thread.win32_thread, u32(exit_code))
}
_yield :: proc() {
+19 -17
View File
@@ -59,28 +59,30 @@ sleep :: proc "contextless" (d: Duration) {
_sleep(d)
}
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
if !running {
_start_time = tick_now()
running = true
stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
if !stopwatch.running {
stopwatch._start_time = tick_now()
stopwatch.running = true
}
}
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
if running {
_accumulation += tick_diff(_start_time, tick_now())
running = false
stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
if stopwatch.running {
stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
stopwatch.running = false
}
}
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
_accumulation = {}
running = false
stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
stopwatch._accumulation = {}
stopwatch.running = false
}
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
if !running { return _accumulation }
return _accumulation + tick_diff(_start_time, tick_now())
stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
if !stopwatch.running {
return stopwatch._accumulation
}
return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
}
diff :: proc "contextless" (start, end: Time) -> Duration {
@@ -171,9 +173,9 @@ day :: proc "contextless" (t: Time) -> (day: int) {
}
weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
abs := _time_abs(t)
sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
return Weekday(int(sec) / SECONDS_PER_DAY)
abs := _time_abs(t)
sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
return Weekday(int(sec) / SECONDS_PER_DAY)
}
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }