mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-07 08:08:50 +00:00
Merge branch 'master' of https://github.com/odin-lang/Odin
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
package xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
@@ -9,7 +11,7 @@
|
|||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
Jeroen van Rijn: Initial implementation.
|
||||||
*/
|
*/
|
||||||
package xml
|
|
||||||
|
|
||||||
import "core:io"
|
import "core:io"
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ example :: proc() {
|
|||||||
xml.destroy(docs[round])
|
xml.destroy(docs[round])
|
||||||
}
|
}
|
||||||
|
|
||||||
DOC :: #load("../../../../tests/core/assets/XML/unicode.xml")
|
DOC :: #load("../../../../tests/core/assets/XML/utf8.xml")
|
||||||
input := DOC
|
input := DOC
|
||||||
|
|
||||||
for round in 0..<N {
|
for round in 0..<N {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
@@ -6,7 +8,7 @@
|
|||||||
|
|
||||||
This file contains helper functions.
|
This file contains helper functions.
|
||||||
*/
|
*/
|
||||||
package xml
|
|
||||||
|
|
||||||
// Find parent's nth child with a given ident.
|
// Find parent's nth child with a given ident.
|
||||||
find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string, nth := 0) -> (res: Element_ID, found: bool) {
|
find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string, nth := 0) -> (res: Element_ID, found: bool) {
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
package xml
|
||||||
|
|
||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
An XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
@@ -9,7 +11,7 @@
|
|||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
Jeroen van Rijn: Initial implementation.
|
||||||
*/
|
*/
|
||||||
package xml
|
|
||||||
|
|
||||||
import "core:fmt"
|
import "core:fmt"
|
||||||
import "core:unicode"
|
import "core:unicode"
|
||||||
|
|||||||
@@ -1,28 +1,28 @@
|
|||||||
/*
|
/*
|
||||||
An XML 1.0 / 1.1 parser
|
XML 1.0 / 1.1 parser
|
||||||
|
|
||||||
Copyright 2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
2021-2022 Jeroen van Rijn <nom@duclavier.com>.
|
||||||
Made available under Odin's BSD-3 license.
|
available under Odin's BSD-3 license.
|
||||||
|
|
||||||
A from-scratch XML implementation, loosely modelled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
|
from-scratch XML implementation, loosely modelled on the [spec](https://www.w3.org/TR/2006/REC-xml11-20060816).
|
||||||
|
|
||||||
Features:
|
Features:
|
||||||
- Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage.
|
- Supports enough of the XML 1.0/1.1 spec to handle the 99.9% of XML documents in common current usage.
|
||||||
- Simple to understand and use. Small.
|
- Simple to understand and use. Small.
|
||||||
|
|
||||||
Caveats:
|
Caveats:
|
||||||
- We do NOT support HTML in this package, as that may or may not be valid XML.
|
- We do NOT support HTML in this package, as that may or may not be valid XML.
|
||||||
If it works, great. If it doesn't, that's not considered a bug.
|
If it works, great. If it doesn't, that's not considered a bug.
|
||||||
|
|
||||||
- We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences.
|
- We do NOT support UTF-16. If you have a UTF-16 XML file, please convert it to UTF-8 first. Also, our condolences.
|
||||||
- <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options.
|
- <[!ELEMENT and <[!ATTLIST are not supported, and will be either ignored or return an error depending on the parser options.
|
||||||
|
|
||||||
MAYBE:
|
MAYBE:
|
||||||
- XML writer?
|
- XML writer?
|
||||||
- Serialize/deserialize Odin types?
|
- Serialize/deserialize Odin types?
|
||||||
|
|
||||||
List of contributors:
|
List of contributors:
|
||||||
Jeroen van Rijn: Initial implementation.
|
- Jeroen van Rijn: Initial implementation.
|
||||||
*/
|
*/
|
||||||
package xml
|
package xml
|
||||||
// An XML 1.0 / 1.1 parser
|
// An XML 1.0 / 1.1 parser
|
||||||
@@ -43,48 +43,32 @@ DEFAULT_OPTIONS :: Options{
|
|||||||
}
|
}
|
||||||
|
|
||||||
Option_Flag :: enum {
|
Option_Flag :: enum {
|
||||||
/*
|
// If the caller says that input may be modified, we can perform in-situ parsing.
|
||||||
If the caller says that input may be modified, we can perform in-situ parsing.
|
// If this flag isn't provided, the XML parser first duplicates the input so that it can.
|
||||||
If this flag isn't provided, the XML parser first duplicates the input so that it can.
|
|
||||||
*/
|
|
||||||
Input_May_Be_Modified,
|
Input_May_Be_Modified,
|
||||||
|
|
||||||
/*
|
// Document MUST start with `<?xml` prologue.
|
||||||
Document MUST start with `<?xml` prologue.
|
|
||||||
*/
|
|
||||||
Must_Have_Prolog,
|
Must_Have_Prolog,
|
||||||
|
|
||||||
/*
|
// Document MUST have a `<!DOCTYPE`.
|
||||||
Document MUST have a `<!DOCTYPE`.
|
|
||||||
*/
|
|
||||||
Must_Have_DocType,
|
Must_Have_DocType,
|
||||||
|
|
||||||
/*
|
// By default we skip comments. Use this option to intern a comment on a parented Element.
|
||||||
By default we skip comments. Use this option to intern a comment on a parented Element.
|
|
||||||
*/
|
|
||||||
Intern_Comments,
|
Intern_Comments,
|
||||||
|
|
||||||
/*
|
// How to handle unsupported parts of the specification, like <! other than <!DOCTYPE and <![CDATA[
|
||||||
How to handle unsupported parts of the specification, like <! other than <!DOCTYPE and <![CDATA[
|
|
||||||
*/
|
|
||||||
Error_on_Unsupported,
|
Error_on_Unsupported,
|
||||||
Ignore_Unsupported,
|
Ignore_Unsupported,
|
||||||
|
|
||||||
/*
|
// By default CDATA tags are passed-through as-is.
|
||||||
By default CDATA tags are passed-through as-is.
|
// This option unwraps them when encountered.
|
||||||
This option unwraps them when encountered.
|
|
||||||
*/
|
|
||||||
Unbox_CDATA,
|
Unbox_CDATA,
|
||||||
|
|
||||||
/*
|
// By default SGML entities like `>`, ` ` and ` ` are passed-through as-is.
|
||||||
By default SGML entities like `>`, ` ` and ` ` are passed-through as-is.
|
// This option decodes them when encountered.
|
||||||
This option decodes them when encountered.
|
|
||||||
*/
|
|
||||||
Decode_SGML_Entities,
|
Decode_SGML_Entities,
|
||||||
|
|
||||||
/*
|
// If a tag body has a comment, it will be stripped unless this option is given.
|
||||||
If a tag body has a comment, it will be stripped unless this option is given.
|
|
||||||
*/
|
|
||||||
Keep_Tag_Body_Comments,
|
Keep_Tag_Body_Comments,
|
||||||
}
|
}
|
||||||
Option_Flags :: bit_set[Option_Flag; u16]
|
Option_Flags :: bit_set[Option_Flag; u16]
|
||||||
@@ -97,28 +81,20 @@ Document :: struct {
|
|||||||
encoding: Encoding,
|
encoding: Encoding,
|
||||||
|
|
||||||
doctype: struct {
|
doctype: struct {
|
||||||
/*
|
// We only scan the <!DOCTYPE IDENT part and skip the rest.
|
||||||
We only scan the <!DOCTYPE IDENT part and skip the rest.
|
|
||||||
*/
|
|
||||||
ident: string,
|
ident: string,
|
||||||
rest: string,
|
rest: string,
|
||||||
},
|
},
|
||||||
|
|
||||||
/*
|
// If we encounter comments before the root node, and the option to intern comments is given, this is where they'll live.
|
||||||
If we encounter comments before the root node, and the option to intern comments is given, this is where they'll live.
|
// Otherwise they'll be in the element tree.
|
||||||
Otherwise they'll be in the element tree.
|
|
||||||
*/
|
|
||||||
comments: [dynamic]string,
|
comments: [dynamic]string,
|
||||||
|
|
||||||
/*
|
// Internal
|
||||||
Internal
|
|
||||||
*/
|
|
||||||
tokenizer: ^Tokenizer,
|
tokenizer: ^Tokenizer,
|
||||||
allocator: mem.Allocator,
|
allocator: mem.Allocator,
|
||||||
|
|
||||||
/*
|
// Input. Either the original buffer, or a copy if `.Input_May_Be_Modified` isn't specified.
|
||||||
Input. Either the original buffer, or a copy if `.Input_May_Be_Modified` isn't specified.
|
|
||||||
*/
|
|
||||||
input: []u8,
|
input: []u8,
|
||||||
strings_to_free: [dynamic]string,
|
strings_to_free: [dynamic]string,
|
||||||
}
|
}
|
||||||
@@ -158,34 +134,24 @@ Encoding :: enum {
|
|||||||
UTF_8,
|
UTF_8,
|
||||||
ISO_8859_1,
|
ISO_8859_1,
|
||||||
|
|
||||||
/*
|
// Aliases
|
||||||
Aliases
|
|
||||||
*/
|
|
||||||
LATIN_1 = ISO_8859_1,
|
LATIN_1 = ISO_8859_1,
|
||||||
}
|
}
|
||||||
|
|
||||||
Error :: enum {
|
Error :: enum {
|
||||||
/*
|
// General return values.
|
||||||
General return values.
|
|
||||||
*/
|
|
||||||
None = 0,
|
None = 0,
|
||||||
General_Error,
|
General_Error,
|
||||||
Unexpected_Token,
|
Unexpected_Token,
|
||||||
Invalid_Token,
|
Invalid_Token,
|
||||||
|
|
||||||
/*
|
// Couldn't find, open or read file.
|
||||||
Couldn't find, open or read file.
|
|
||||||
*/
|
|
||||||
File_Error,
|
File_Error,
|
||||||
|
|
||||||
/*
|
// File too short.
|
||||||
File too short.
|
|
||||||
*/
|
|
||||||
Premature_EOF,
|
Premature_EOF,
|
||||||
|
|
||||||
/*
|
// XML-specific errors.
|
||||||
XML-specific errors.
|
|
||||||
*/
|
|
||||||
No_Prolog,
|
No_Prolog,
|
||||||
Invalid_Prolog,
|
Invalid_Prolog,
|
||||||
Too_Many_Prologs,
|
Too_Many_Prologs,
|
||||||
@@ -194,11 +160,9 @@ Error :: enum {
|
|||||||
Too_Many_DocTypes,
|
Too_Many_DocTypes,
|
||||||
DocType_Must_Preceed_Elements,
|
DocType_Must_Preceed_Elements,
|
||||||
|
|
||||||
/*
|
// If a DOCTYPE is present _or_ the caller
|
||||||
If a DOCTYPE is present _or_ the caller
|
// asked for a specific DOCTYPE and the DOCTYPE
|
||||||
asked for a specific DOCTYPE and the DOCTYPE
|
// and root tag don't match, we return `.Invalid_DocType`.
|
||||||
and root tag don't match, we return `.Invalid_DocType`.
|
|
||||||
*/
|
|
||||||
Invalid_DocType,
|
Invalid_DocType,
|
||||||
|
|
||||||
Invalid_Tag_Value,
|
Invalid_Tag_Value,
|
||||||
@@ -211,27 +175,20 @@ Error :: enum {
|
|||||||
Unsupported_Version,
|
Unsupported_Version,
|
||||||
Unsupported_Encoding,
|
Unsupported_Encoding,
|
||||||
|
|
||||||
/*
|
// <!FOO are usually skipped.
|
||||||
<!FOO are usually skipped.
|
|
||||||
*/
|
|
||||||
Unhandled_Bang,
|
Unhandled_Bang,
|
||||||
|
|
||||||
Duplicate_Attribute,
|
Duplicate_Attribute,
|
||||||
Conflicting_Options,
|
Conflicting_Options,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
Implementation starts here.
|
|
||||||
*/
|
|
||||||
parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_handler := default_error_handler, allocator := context.allocator) -> (doc: ^Document, err: Error) {
|
||||||
data := data
|
data := data
|
||||||
context.allocator = allocator
|
context.allocator = allocator
|
||||||
|
|
||||||
opts := validate_options(options) or_return
|
opts := validate_options(options) or_return
|
||||||
|
|
||||||
/*
|
// If `.Input_May_Be_Modified` is not specified, we duplicate the input so that we can modify it in-place.
|
||||||
If `.Input_May_Be_Modified` is not specified, we duplicate the input so that we can modify it in-place.
|
|
||||||
*/
|
|
||||||
if .Input_May_Be_Modified not_in opts.flags {
|
if .Input_May_Be_Modified not_in opts.flags {
|
||||||
data = bytes.clone(data)
|
data = bytes.clone(data)
|
||||||
}
|
}
|
||||||
@@ -252,10 +209,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
element, parent: Element_ID
|
element, parent: Element_ID
|
||||||
open: Token
|
open: Token
|
||||||
|
|
||||||
/*
|
// If a DOCTYPE is present, the root tag has to match.
|
||||||
If a DOCTYPE is present, the root tag has to match.
|
// If an expected DOCTYPE is given in options (i.e. it's non-empty), the DOCTYPE (if present) and root tag have to match.
|
||||||
If an expected DOCTYPE is given in options (i.e. it's non-empty), the DOCTYPE (if present) and root tag have to match.
|
|
||||||
*/
|
|
||||||
expected_doctype := options.expected_doctype
|
expected_doctype := options.expected_doctype
|
||||||
|
|
||||||
loop: for {
|
loop: for {
|
||||||
@@ -263,17 +218,13 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
// NOTE(Jeroen): This is faster as a switch.
|
// NOTE(Jeroen): This is faster as a switch.
|
||||||
switch t.ch {
|
switch t.ch {
|
||||||
case '<':
|
case '<':
|
||||||
/*
|
// Consume peeked `<`
|
||||||
Consume peeked `<`
|
|
||||||
*/
|
|
||||||
advance_rune(t)
|
advance_rune(t)
|
||||||
|
|
||||||
open = scan(t)
|
open = scan(t)
|
||||||
// NOTE(Jeroen): We're not using a switch because this if-else chain ordered by likelihood is 2.5% faster at -o:size and -o:speed.
|
// NOTE(Jeroen): We're not using a switch because this if-else chain ordered by likelihood is 2.5% faster at -o:size and -o:speed.
|
||||||
if likely(open.kind, Token_Kind.Ident) == .Ident {
|
if likely(open.kind, Token_Kind.Ident) == .Ident {
|
||||||
/*
|
// e.g. <odin - Start of new element.
|
||||||
e.g. <odin - Start of new element.
|
|
||||||
*/
|
|
||||||
element = new_element(doc)
|
element = new_element(doc)
|
||||||
if element == 0 { // First Element
|
if element == 0 { // First Element
|
||||||
parent = element
|
parent = element
|
||||||
@@ -286,11 +237,9 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
|
|
||||||
parse_attributes(doc, &doc.elements[element].attribs) or_return
|
parse_attributes(doc, &doc.elements[element].attribs) or_return
|
||||||
|
|
||||||
/*
|
// If a DOCTYPE is present _or_ the caller
|
||||||
If a DOCTYPE is present _or_ the caller
|
// asked for a specific DOCTYPE and the DOCTYPE
|
||||||
asked for a specific DOCTYPE and the DOCTYPE
|
// and root tag don't match, we return .Invalid_Root_Tag.
|
||||||
and root tag don't match, we return .Invalid_Root_Tag.
|
|
||||||
*/
|
|
||||||
if element == 0 { // Root tag?
|
if element == 0 { // Root tag?
|
||||||
if len(expected_doctype) > 0 && expected_doctype != open.text {
|
if len(expected_doctype) > 0 && expected_doctype != open.text {
|
||||||
error(t, t.offset, "Root Tag doesn't match DOCTYPE. Expected: %v, got: %v\n", expected_doctype, open.text)
|
error(t, t.offset, "Root Tag doesn't match DOCTYPE. Expected: %v, got: %v\n", expected_doctype, open.text)
|
||||||
@@ -298,23 +247,17 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
// One of these should follow:
|
||||||
One of these should follow:
|
// - `>`, which means we've just opened this tag and expect a later element to close it.
|
||||||
- `>`, which means we've just opened this tag and expect a later element to close it.
|
// - `/>`, which means this is an 'empty' or self-closing tag.
|
||||||
- `/>`, which means this is an 'empty' or self-closing tag.
|
|
||||||
*/
|
|
||||||
end_token := scan(t)
|
end_token := scan(t)
|
||||||
#partial switch end_token.kind {
|
#partial switch end_token.kind {
|
||||||
case .Gt:
|
case .Gt:
|
||||||
/*
|
// We're now the new parent.
|
||||||
We're now the new parent.
|
|
||||||
*/
|
|
||||||
parent = element
|
parent = element
|
||||||
|
|
||||||
case .Slash:
|
case .Slash:
|
||||||
/*
|
// Empty tag. Close it.
|
||||||
Empty tag. Close it.
|
|
||||||
*/
|
|
||||||
expect(t, .Gt) or_return
|
expect(t, .Gt) or_return
|
||||||
parent = doc.elements[element].parent
|
parent = doc.elements[element].parent
|
||||||
element = parent
|
element = parent
|
||||||
@@ -325,9 +268,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if open.kind == .Slash {
|
} else if open.kind == .Slash {
|
||||||
/*
|
// Close tag.
|
||||||
Close tag.
|
|
||||||
*/
|
|
||||||
ident := expect(t, .Ident) or_return
|
ident := expect(t, .Ident) or_return
|
||||||
_ = expect(t, .Gt) or_return
|
_ = expect(t, .Gt) or_return
|
||||||
|
|
||||||
@@ -339,9 +280,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
element = parent
|
element = parent
|
||||||
|
|
||||||
} else if open.kind == .Exclaim {
|
} else if open.kind == .Exclaim {
|
||||||
/*
|
// <!
|
||||||
<!
|
|
||||||
*/
|
|
||||||
next := scan(t)
|
next := scan(t)
|
||||||
#partial switch next.kind {
|
#partial switch next.kind {
|
||||||
case .Ident:
|
case .Ident:
|
||||||
@@ -370,10 +309,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
case .Dash:
|
case .Dash:
|
||||||
/*
|
// Comment: <!-- -->.
|
||||||
Comment: <!-- -->.
|
// The grammar does not allow a comment to end in --->
|
||||||
The grammar does not allow a comment to end in --->
|
|
||||||
*/
|
|
||||||
expect(t, .Dash)
|
expect(t, .Dash)
|
||||||
comment := scan_comment(t) or_return
|
comment := scan_comment(t) or_return
|
||||||
|
|
||||||
@@ -395,23 +332,17 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
} else if open.kind == .Question {
|
} else if open.kind == .Question {
|
||||||
/*
|
// <?xml
|
||||||
<?xml
|
|
||||||
*/
|
|
||||||
next := scan(t)
|
next := scan(t)
|
||||||
#partial switch next.kind {
|
#partial switch next.kind {
|
||||||
case .Ident:
|
case .Ident:
|
||||||
if len(next.text) == 3 && strings.equal_fold(next.text, "xml") {
|
if len(next.text) == 3 && strings.equal_fold(next.text, "xml") {
|
||||||
parse_prologue(doc) or_return
|
parse_prologue(doc) or_return
|
||||||
} else if len(doc.prologue) > 0 {
|
} else if len(doc.prologue) > 0 {
|
||||||
/*
|
// We've already seen a prologue.
|
||||||
We've already seen a prologue.
|
|
||||||
*/
|
|
||||||
return doc, .Too_Many_Prologs
|
return doc, .Too_Many_Prologs
|
||||||
} else {
|
} else {
|
||||||
/*
|
// Could be `<?xml-stylesheet`, etc. Ignore it.
|
||||||
Could be `<?xml-stylesheet`, etc. Ignore it.
|
|
||||||
*/
|
|
||||||
skip_element(t) or_return
|
skip_element(t) or_return
|
||||||
}
|
}
|
||||||
case:
|
case:
|
||||||
@@ -425,15 +356,11 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
|||||||
}
|
}
|
||||||
|
|
||||||
case -1:
|
case -1:
|
||||||
/*
|
// End of file.
|
||||||
End of file.
|
|
||||||
*/
|
|
||||||
break loop
|
break loop
|
||||||
|
|
||||||
case:
|
case:
|
||||||
/*
|
// This should be a tag's body text.
|
||||||
This should be a tag's body text.
|
|
||||||
*/
|
|
||||||
body_text := scan_string(t, t.offset) or_return
|
body_text := scan_string(t, t.offset) or_return
|
||||||
needs_processing := .Unbox_CDATA in opts.flags
|
needs_processing := .Unbox_CDATA in opts.flags
|
||||||
needs_processing |= .Decode_SGML_Entities in opts.flags
|
needs_processing |= .Decode_SGML_Entities in opts.flags
|
||||||
@@ -613,9 +540,7 @@ parse_prologue :: proc(doc: ^Document) -> (err: Error) {
|
|||||||
doc.encoding = .LATIN_1
|
doc.encoding = .LATIN_1
|
||||||
|
|
||||||
case:
|
case:
|
||||||
/*
|
// Unrecognized encoding, assume UTF-8.
|
||||||
Unrecognized encoding, assume UTF-8.
|
|
||||||
*/
|
|
||||||
error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
|
error(t, offset, "[parse_prologue] Warning: Unrecognized encoding: %v\n", attr.val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -675,9 +600,7 @@ parse_doctype :: proc(doc: ^Document) -> (err: Error) {
|
|||||||
offset := t.offset
|
offset := t.offset
|
||||||
skip_element(t) or_return
|
skip_element(t) or_return
|
||||||
|
|
||||||
/*
|
// -1 because the current offset is that of the closing tag, so the rest of the DOCTYPE tag ends just before it.
|
||||||
-1 because the current offset is that of the closing tag, so the rest of the DOCTYPE tag ends just before it.
|
|
||||||
*/
|
|
||||||
doc.doctype.rest = string(t.src[offset : t.offset - 1])
|
doc.doctype.rest = string(t.src[offset : t.offset - 1])
|
||||||
return .None
|
return .None
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -96,6 +96,15 @@ log_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mode,
|
|||||||
str := fmt.bprintf(buf[:], format, la.prefix, padding, old_memory, old_size, size, alignment)
|
str := fmt.bprintf(buf[:], format, la.prefix, padding, old_memory, old_size, size, alignment)
|
||||||
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
|
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
format: string
|
||||||
|
switch la.size_fmt {
|
||||||
|
case .Bytes: format = "%s%s>>> ALLOCATOR(mode=.Resize_Non_Zeroed, ptr=%p, old_size=%d, size=%d, alignment=%d)"
|
||||||
|
case .Human: format = "%s%s>>> ALLOCATOR(mode=.Resize_Non_Zeroed, ptr=%p, old_size=%m, size=%m, alignment=%d)"
|
||||||
|
}
|
||||||
|
str := fmt.bprintf(buf[:], format, la.prefix, padding, old_memory, old_size, size, alignment)
|
||||||
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
str := fmt.bprintf(buf[:], "%s%sALLOCATOR(mode=.Query_Features)", la.prefix, padding)
|
str := fmt.bprintf(buf[:], "%s%sALLOCATOR(mode=.Query_Features)", la.prefix, padding)
|
||||||
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
context.logger.procedure(context.logger.data, la.level, str, context.logger.options, location)
|
||||||
|
|||||||
@@ -88,17 +88,19 @@ div_sat :: proc(x, y: $T/Fixed($Backing, $Fraction_Width)) -> (z: T) {
|
|||||||
|
|
||||||
@(require_results)
|
@(require_results)
|
||||||
floor :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
floor :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||||
|
if x.i >= 0 {
|
||||||
return x.i >> Fraction_Width
|
return x.i >> Fraction_Width
|
||||||
|
} else {
|
||||||
|
return (x.i - (1 << (Fraction_Width - 1)) + (1 << (Fraction_Width - 2))) >> Fraction_Width
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
ceil :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
ceil :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||||
Integer :: 8*size_of(Backing) - Fraction_Width
|
return (x.i + (1 << Fraction_Width - 1)) >> Fraction_Width
|
||||||
return (x.i + (1 << Integer-1)) >> Fraction_Width
|
|
||||||
}
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
round :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> Backing {
|
||||||
Integer :: 8*size_of(Backing) - Fraction_Width
|
return (x.i + (1 << (Fraction_Width - 1))) >> Fraction_Width
|
||||||
return (x.i + (1 << (Integer - 1))) >> Fraction_Width
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+23
-1
@@ -11,6 +11,8 @@ Allocator_Mode :: enum byte {
|
|||||||
Free_All,
|
Free_All,
|
||||||
Resize,
|
Resize,
|
||||||
Query_Features,
|
Query_Features,
|
||||||
|
Alloc_Non_Zeroed,
|
||||||
|
Resize_Non_Zeroed,
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@@ -243,12 +245,26 @@ default_resize_align :: proc(old_memory: rawptr, old_size, new_size, alignment:
|
|||||||
res = raw_data(data)
|
res = raw_data(data)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
default_resize_bytes_align_non_zeroed :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
return _default_resize_bytes_align(old_data, new_size, alignment, false, allocator, loc)
|
||||||
|
}
|
||||||
@(require_results)
|
@(require_results)
|
||||||
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
|
return _default_resize_bytes_align(old_data, new_size, alignment, true, allocator, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@(require_results)
|
||||||
|
_default_resize_bytes_align :: #force_inline proc(old_data: []byte, new_size, alignment: int, should_zero: bool, allocator := context.allocator, loc := #caller_location) -> ([]byte, Allocator_Error) {
|
||||||
old_memory := raw_data(old_data)
|
old_memory := raw_data(old_data)
|
||||||
old_size := len(old_data)
|
old_size := len(old_data)
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
|
if should_zero {
|
||||||
return alloc_bytes(new_size, alignment, allocator, loc)
|
return alloc_bytes(new_size, alignment, allocator, loc)
|
||||||
|
} else {
|
||||||
|
return alloc_bytes_non_zeroed(new_size, alignment, allocator, loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if new_size == 0 {
|
if new_size == 0 {
|
||||||
@@ -260,7 +276,13 @@ default_resize_bytes_align :: proc(old_data: []byte, new_size, alignment: int, a
|
|||||||
return old_data, .None
|
return old_data, .None
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory, err := alloc_bytes(new_size, alignment, allocator, loc)
|
new_memory : []byte
|
||||||
|
err : Allocator_Error
|
||||||
|
if should_zero {
|
||||||
|
new_memory, err = alloc_bytes(new_size, alignment, allocator, loc)
|
||||||
|
} else {
|
||||||
|
new_memory, err = alloc_bytes_non_zeroed(new_size, alignment, allocator, loc)
|
||||||
|
}
|
||||||
if new_memory == nil || err != nil {
|
if new_memory == nil || err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
+21
-14
@@ -88,10 +88,13 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Resize:
|
case .Resize:
|
||||||
return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena))
|
return default_resize_bytes_align(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena))
|
||||||
|
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
return default_resize_bytes_align_non_zeroed(byte_slice(old_memory, old_size), size, alignment, arena_allocator(arena))
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
@@ -259,7 +262,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
}
|
}
|
||||||
clear(&s.leaked_allocations)
|
clear(&s.leaked_allocations)
|
||||||
|
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
begin := uintptr(raw_data(s.data))
|
begin := uintptr(raw_data(s.data))
|
||||||
end := begin + uintptr(len(s.data))
|
end := begin + uintptr(len(s.data))
|
||||||
old_ptr := uintptr(old_memory)
|
old_ptr := uintptr(old_memory)
|
||||||
@@ -278,7 +281,7 @@ scratch_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
@@ -406,9 +409,9 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
s.prev_offset = 0
|
s.prev_offset = 0
|
||||||
s.curr_offset = 0
|
s.curr_offset = 0
|
||||||
|
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return raw_alloc(s, size, alignment, true)
|
return raw_alloc(s, size, alignment, mode == .Resize)
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -434,7 +437,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
|
old_offset := int(curr_addr - uintptr(header.padding) - uintptr(raw_data(s.data)))
|
||||||
|
|
||||||
if old_offset != header.prev_offset {
|
if old_offset != header.prev_offset {
|
||||||
data, err := raw_alloc(s, size, alignment, true)
|
data, err := raw_alloc(s, size, alignment, mode == .Resize)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||||
}
|
}
|
||||||
@@ -455,7 +458,7 @@ stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case .Query_Info:
|
case .Query_Info:
|
||||||
@@ -565,9 +568,9 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Free_All:
|
case .Free_All:
|
||||||
s.offset = 0
|
s.offset = 0
|
||||||
|
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return raw_alloc(s, size, align, true)
|
return raw_alloc(s, size, align, mode == .Resize)
|
||||||
}
|
}
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
@@ -590,7 +593,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return byte_slice(old_memory, size), nil
|
return byte_slice(old_memory, size), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
data, err := raw_alloc(s, size, align, true)
|
data, err := raw_alloc(s, size, align, mode == .Resize)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
runtime.copy(data, byte_slice(old_memory, old_size))
|
runtime.copy(data, byte_slice(old_memory, old_size))
|
||||||
}
|
}
|
||||||
@@ -599,7 +602,7 @@ small_stack_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Query_Features}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
@@ -649,7 +652,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
|||||||
case .Free_All:
|
case .Free_All:
|
||||||
dynamic_pool_free_all(pool)
|
dynamic_pool_free_all(pool)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if old_size >= size {
|
if old_size >= size {
|
||||||
return byte_slice(old_memory, size), nil
|
return byte_slice(old_memory, size), nil
|
||||||
}
|
}
|
||||||
@@ -662,7 +665,7 @@ dynamic_pool_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode
|
|||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^Allocator_Mode_Set)(old_memory)
|
set := (^Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Query_Features, .Query_Info}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free_All, .Resize, .Resize_Non_Zeroed, .Query_Features, .Query_Info}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
@@ -826,6 +829,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if size > 0 {
|
if size > 0 {
|
||||||
panic("mem: panic allocator, .Resize called", loc=loc)
|
panic("mem: panic allocator, .Resize called", loc=loc)
|
||||||
}
|
}
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
if size > 0 {
|
||||||
|
panic("mem: panic allocator, .Resize_Non_Zeroed called", loc=loc)
|
||||||
|
}
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory != nil {
|
if old_memory != nil {
|
||||||
panic("mem: panic allocator, .Free called", loc=loc)
|
panic("mem: panic allocator, .Free called", loc=loc)
|
||||||
@@ -958,7 +965,7 @@ tracking_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if data.clear_on_free_all {
|
if data.clear_on_free_all {
|
||||||
clear_map(&data.allocation_map)
|
clear_map(&data.allocation_map)
|
||||||
}
|
}
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if old_memory != result_ptr {
|
if old_memory != result_ptr {
|
||||||
delete_key(&data.allocation_map, old_memory)
|
delete_key(&data.allocation_map, old_memory)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
|||||||
err = .Mode_Not_Implemented
|
err = .Mode_Not_Implemented
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
arena_free_all(arena, location)
|
arena_free_all(arena, location)
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
old_data := ([^]byte)(old_memory)
|
old_data := ([^]byte)(old_memory)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
+7
-7
@@ -210,15 +210,15 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int) -> (new_memory: []byte, err: mem.Allocator_Error) {
|
aligned_resize :: proc(p: rawptr, old_size: int, new_size: int, new_alignment: int, zero_memory := true) -> (new_memory: []byte, err: mem.Allocator_Error) {
|
||||||
if p == nil {
|
if p == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
new_memory = aligned_alloc(new_size, new_alignment, p) or_return
|
new_memory = aligned_alloc(new_size, new_alignment, p, zero_memory) or_return
|
||||||
|
|
||||||
// NOTE: heap_resize does not zero the new memory, so we do it
|
// NOTE: heap_resize does not zero the new memory, so we do it
|
||||||
if new_size > old_size {
|
if zero_memory && new_size > old_size {
|
||||||
new_region := mem.raw_data(new_memory[old_size:])
|
new_region := mem.raw_data(new_memory[old_size:])
|
||||||
mem.zero(new_region, new_size - old_size)
|
mem.zero(new_region, new_size - old_size)
|
||||||
}
|
}
|
||||||
@@ -235,16 +235,16 @@ heap_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
|||||||
case .Free_All:
|
case .Free_All:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if old_memory == nil {
|
if old_memory == nil {
|
||||||
return aligned_alloc(size, alignment)
|
return aligned_alloc(size, alignment, nil, mode == .Resize)
|
||||||
}
|
}
|
||||||
return aligned_resize(old_memory, old_size, size, alignment)
|
return aligned_resize(old_memory, old_size, size, alignment, mode == .Resize)
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
set := (^mem.Allocator_Mode_Set)(old_memory)
|
set := (^mem.Allocator_Mode_Set)(old_memory)
|
||||||
if set != nil {
|
if set != nil {
|
||||||
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Query_Features}
|
set^ = {.Alloc, .Alloc_Non_Zeroed, .Free, .Resize, .Resize_Non_Zeroed, .Query_Features}
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
|
|||||||
@@ -307,6 +307,7 @@ Allocator_Mode :: enum byte {
|
|||||||
Query_Features,
|
Query_Features,
|
||||||
Query_Info,
|
Query_Info,
|
||||||
Alloc_Non_Zeroed,
|
Alloc_Non_Zeroed,
|
||||||
|
Resize_Non_Zeroed,
|
||||||
}
|
}
|
||||||
|
|
||||||
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
Allocator_Mode_Set :: distinct bit_set[Allocator_Mode]
|
||||||
|
|||||||
@@ -169,10 +169,16 @@ clear :: proc{clear_dynamic_array, clear_map}
|
|||||||
@builtin
|
@builtin
|
||||||
reserve :: proc{reserve_dynamic_array, reserve_map}
|
reserve :: proc{reserve_dynamic_array, reserve_map}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_reserve :: proc{non_zero_reserve_dynamic_array}
|
||||||
|
|
||||||
// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
// `resize` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
@builtin
|
@builtin
|
||||||
resize :: proc{resize_dynamic_array}
|
resize :: proc{resize_dynamic_array}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize :: proc{non_zero_resize_dynamic_array}
|
||||||
|
|
||||||
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
// Shrinks the capacity of a dynamic array or map down to the current length, or the given capacity.
|
||||||
@builtin
|
@builtin
|
||||||
shrink :: proc{shrink_dynamic_array, shrink_map}
|
shrink :: proc{shrink_dynamic_array, shrink_map}
|
||||||
@@ -406,10 +412,7 @@ delete_key :: proc(m: ^$T/map[$K]$V, key: K) -> (deleted_key: K, deleted_value:
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_append_elem :: #force_inline proc(array: ^$T/[dynamic]$E, arg: E, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
|
||||||
@builtin
|
|
||||||
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -420,7 +423,13 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
|||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+1 {
|
if cap(array) < len(array)+1 {
|
||||||
cap := 2 * cap(array) + max(8, 1)
|
cap := 2 * cap(array) + max(8, 1)
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
if should_zero {
|
||||||
|
err = reserve(array, cap, loc)
|
||||||
|
} else {
|
||||||
|
err = non_zero_reserve(array, cap, loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if cap(array)-len(array) > 0 {
|
if cap(array)-len(array) > 0 {
|
||||||
a := (^Raw_Dynamic_Array)(array)
|
a := (^Raw_Dynamic_Array)(array)
|
||||||
@@ -437,7 +446,16 @@ append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) ->
|
|||||||
}
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem(array, arg, true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elem :: proc(array: ^$T/[dynamic]$E, arg: E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem(array, arg, false, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
_append_elems :: #force_inline proc(array: ^$T/[dynamic]$E, should_zero: bool, loc := #caller_location, args: ..E) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return 0, nil
|
return 0, nil
|
||||||
}
|
}
|
||||||
@@ -454,7 +472,13 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
|||||||
} else {
|
} else {
|
||||||
if cap(array) < len(array)+arg_len {
|
if cap(array) < len(array)+arg_len {
|
||||||
cap := 2 * cap(array) + max(8, arg_len)
|
cap := 2 * cap(array) + max(8, arg_len)
|
||||||
err = reserve(array, cap, loc) // do not 'or_return' here as it could be a partial success
|
|
||||||
|
// do not 'or_return' here as it could be a partial success
|
||||||
|
if should_zero {
|
||||||
|
err = reserve(array, cap, loc)
|
||||||
|
} else {
|
||||||
|
err = non_zero_reserve(array, cap, loc)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
arg_len = min(cap(array)-len(array), arg_len)
|
arg_len = min(cap(array)-len(array), arg_len)
|
||||||
if arg_len > 0 {
|
if arg_len > 0 {
|
||||||
@@ -470,11 +494,33 @@ append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elems(array, true, loc, ..args)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elems :: proc(array: ^$T/[dynamic]$E, args: ..E, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elems(array, false, loc, ..args)
|
||||||
|
}
|
||||||
|
|
||||||
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
// The append_string built-in procedure appends a string to the end of a [dynamic]u8 like type
|
||||||
|
_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, should_zero: bool, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
args := transmute([]E)arg
|
||||||
|
if should_zero {
|
||||||
|
return append_elems(array, ..args, loc=loc)
|
||||||
|
} else {
|
||||||
|
return non_zero_append_elems(array, ..args, loc=loc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
args := transmute([]E)arg
|
return _append_elem_string(array, arg, true, loc)
|
||||||
return append_elems(array, ..args, loc=loc)
|
}
|
||||||
|
@builtin
|
||||||
|
non_zero_append_elem_string :: proc(array: ^$T/[dynamic]$E/u8, arg: $A/string, loc := #caller_location) -> (n: int, err: Allocator_Error) #optional_allocator_error {
|
||||||
|
return _append_elem_string(array, arg, false, loc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -494,6 +540,7 @@ append_string :: proc(array: ^$T/[dynamic]$E/u8, args: ..string, loc := #caller_
|
|||||||
|
|
||||||
// The append built-in procedure appends elements to the end of a dynamic array
|
// The append built-in procedure appends elements to the end of a dynamic array
|
||||||
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
@builtin append :: proc{append_elem, append_elems, append_elem_string}
|
||||||
|
@builtin non_zero_append :: proc{non_zero_append_elem, non_zero_append_elems, non_zero_append_elem_string}
|
||||||
|
|
||||||
|
|
||||||
@builtin
|
@builtin
|
||||||
@@ -638,8 +685,7 @@ clear_dynamic_array :: proc "contextless" (array: ^$T/[dynamic]$E) {
|
|||||||
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
// `reserve_dynamic_array` will try to reserve memory of a passed dynamic array or map to the requested element count (setting the `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `reserve`.
|
// Note: Prefer the procedure group `reserve`.
|
||||||
@builtin
|
_reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -658,7 +704,12 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
new_size := capacity * size_of(E)
|
new_size := capacity * size_of(E)
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data: []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -668,11 +719,20 @@ reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #cal
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_dynamic_array(array, capacity, true, loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _reserve_dynamic_array(array, capacity, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
// `resize_dynamic_array` will try to resize memory of a passed dynamic array or map to the requested element count (setting the `len`, and possibly `cap`).
|
||||||
//
|
//
|
||||||
// Note: Prefer the procedure group `resize`
|
// Note: Prefer the procedure group `resize`
|
||||||
@builtin
|
_resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int, should_zero: bool, loc := #caller_location) -> Allocator_Error {
|
||||||
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
|
||||||
if array == nil {
|
if array == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -692,7 +752,12 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
new_size := length * size_of(E)
|
new_size := length * size_of(E)
|
||||||
allocator := a.allocator
|
allocator := a.allocator
|
||||||
|
|
||||||
new_data := mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
new_data : []byte
|
||||||
|
if should_zero {
|
||||||
|
new_data = mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
} else {
|
||||||
|
new_data = non_zero_mem_resize(a.data, old_size, new_size, align_of(E), allocator, loc) or_return
|
||||||
|
}
|
||||||
if new_data == nil && new_size > 0 {
|
if new_data == nil && new_size > 0 {
|
||||||
return .Out_Of_Memory
|
return .Out_Of_Memory
|
||||||
}
|
}
|
||||||
@@ -703,6 +768,16 @@ resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array(array, length, true, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@builtin
|
||||||
|
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error {
|
||||||
|
return _resize_dynamic_array(array, length, false, loc=loc)
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
Shrinks the capacity of a dynamic array down to the current length, or the given capacity.
|
||||||
|
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ arena_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
err = .Mode_Not_Implemented
|
err = .Mode_Not_Implemented
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
arena_free_all(arena, location)
|
arena_free_all(arena, location)
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
old_data := ([^]byte)(old_memory)
|
old_data := ([^]byte)(old_memory)
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ nil_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
return nil, .None
|
return nil, .None
|
||||||
case .Free_All:
|
case .Free_All:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
if size == 0 {
|
if size == 0 {
|
||||||
return nil, .None
|
return nil, .None
|
||||||
}
|
}
|
||||||
@@ -55,6 +55,10 @@ panic_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
|
|||||||
if size > 0 {
|
if size > 0 {
|
||||||
panic("panic allocator, .Resize called", loc=loc)
|
panic("panic allocator, .Resize called", loc=loc)
|
||||||
}
|
}
|
||||||
|
case .Resize_Non_Zeroed:
|
||||||
|
if size > 0 {
|
||||||
|
panic("panic allocator, .Alloc_Non_Zeroed called", loc=loc)
|
||||||
|
}
|
||||||
case .Free:
|
case .Free:
|
||||||
if old_memory != nil {
|
if old_memory != nil {
|
||||||
panic("panic allocator, .Free called", loc=loc)
|
panic("panic allocator, .Free called", loc=loc)
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
|||||||
case .Free_All:
|
case .Free_All:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
data, err = _windows_default_resize(old_memory, old_size, size, alignment)
|
data, err = _windows_default_resize(old_memory, old_size, size, alignment)
|
||||||
|
|
||||||
case .Query_Features:
|
case .Query_Features:
|
||||||
|
|||||||
@@ -187,7 +187,7 @@ mem_free_all :: #force_inline proc(allocator := context.allocator, loc := #calle
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
_mem_resize :: #force_inline proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, should_zero: bool, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
if allocator.procedure == nil {
|
if allocator.procedure == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
@@ -198,15 +198,27 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if ptr == nil {
|
} else if ptr == nil {
|
||||||
|
if should_zero {
|
||||||
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
return allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
return allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
} else if old_size == new_size && uintptr(ptr) % uintptr(alignment) == 0 {
|
||||||
data = ([^]byte)(ptr)[:old_size]
|
data = ([^]byte)(ptr)[:old_size]
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if should_zero {
|
||||||
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
data, err = allocator.procedure(allocator.data, .Resize, new_size, alignment, ptr, old_size, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Resize_Non_Zeroed, new_size, alignment, ptr, old_size, loc)
|
||||||
|
}
|
||||||
if err == .Mode_Not_Implemented {
|
if err == .Mode_Not_Implemented {
|
||||||
|
if should_zero {
|
||||||
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
data, err = allocator.procedure(allocator.data, .Alloc, new_size, alignment, nil, 0, loc)
|
||||||
|
} else {
|
||||||
|
data, err = allocator.procedure(allocator.data, .Alloc_Non_Zeroed, new_size, alignment, nil, 0, loc)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -216,6 +228,13 @@ mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAUL
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, true, loc)
|
||||||
|
}
|
||||||
|
non_zero_mem_resize :: proc(ptr: rawptr, old_size, new_size: int, alignment: int = DEFAULT_ALIGNMENT, allocator := context.allocator, loc := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||||
|
return _mem_resize(ptr, old_size, new_size, alignment, allocator, false, loc)
|
||||||
|
}
|
||||||
|
|
||||||
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||||
switch {
|
switch {
|
||||||
case n == 0: return true
|
case n == 0: return true
|
||||||
|
|||||||
Vendored
+1
-1
@@ -511,7 +511,7 @@ cmark_allocator_proc :: proc(allocator_data: rawptr, mode: runtime.Allocator_Mod
|
|||||||
case .Free_All:
|
case .Free_All:
|
||||||
return nil, .Mode_Not_Implemented
|
return nil, .Mode_Not_Implemented
|
||||||
|
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
new_ptr := cmark_alloc.realloc(old_memory, c.size_t(size))
|
new_ptr := cmark_alloc.realloc(old_memory, c.size_t(size))
|
||||||
res = transmute([]byte)runtime.Raw_Slice{new_ptr, size}
|
res = transmute([]byte)runtime.Raw_Slice{new_ptr, size}
|
||||||
if size > old_size {
|
if size > old_size {
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1774,7 +1774,7 @@ MemAllocatorProc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
|||||||
MemFree(old_memory)
|
MemFree(old_memory)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
|
||||||
case .Resize:
|
case .Resize, .Resize_Non_Zeroed:
|
||||||
ptr := MemRealloc(old_memory, c.uint(size))
|
ptr := MemRealloc(old_memory, c.uint(size))
|
||||||
if ptr == nil {
|
if ptr == nil {
|
||||||
err = .Out_Of_Memory
|
err = .Out_Of_Memory
|
||||||
|
|||||||
Reference in New Issue
Block a user