diff --git a/core/strconv/decimal/decimal.odin b/core/strconv/decimal/decimal.odin index 24ad2ce91..bea5d4452 100644 --- a/core/strconv/decimal/decimal.odin +++ b/core/strconv/decimal/decimal.odin @@ -8,7 +8,17 @@ Decimal :: struct { decimal_point: int, neg, trunc: bool, } +/* +Sets a Decimal from a given string `s`. The string is expected to represent a float. Stores parsed number in the given Decimal structure. +If parsing fails, the Decimal will be left in an undefined state. +**Inputs** +- d: Pointer to a Decimal struct where the parsed result will be stored +- s: The input string representing the floating-point number + +**Returns** +- ok: A boolean indicating whether the parsing was successful +*/ set :: proc(d: ^Decimal, s: string) -> (ok: bool) { d^ = {} @@ -91,7 +101,16 @@ set :: proc(d: ^Decimal, s: string) -> (ok: bool) { return i == len(s) } +/* +Converts a Decimal to a string representation, using the provided buffer as storage. +**Inputs** +- buf: A byte slice buffer to hold the resulting string +- a: The struct to be converted to a string + +**Returns** +- A string representation of the Decimal +*/ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string { digit_zero :: proc(buf: []byte) -> int { for _, i in buf { @@ -100,7 +119,6 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string { return len(buf) } - n := 10 + a.count + abs(a.decimal_point) // TODO(bill): make this work with a buffer that's not big enough @@ -129,8 +147,12 @@ decimal_to_string :: proc(buf: []byte, a: ^Decimal) -> string { return string(b[0:w]) } +/* +Trims trailing zeros in the given Decimal, updating the count and decimal_point values as needed. -// trim trailing zeros +**Inputs** +- a: Pointer to the Decimal struct to be trimmed +*/ trim :: proc(a: ^Decimal) { for a.count > 0 && a.digits[a.count-1] == '0' { a.count -= 1 @@ -139,8 +161,15 @@ trim :: proc(a: ^Decimal) { a.decimal_point = 0 } } +/* +Converts a given u64 integer `idx` to its Decimal representation in the provided Decimal struct. +**Used for internal Decimal Operations.** +**Inputs** +- a: Where the result will be stored +- idx: The value to be assigned to the Decimal +*/ assign :: proc(a: ^Decimal, idx: u64) { buf: [64]byte n := 0 @@ -160,9 +189,15 @@ assign :: proc(a: ^Decimal, idx: u64) { a.decimal_point = a.count trim(a) } +/* +Shifts the Decimal value to the right by k positions. +**Used for internal Decimal Operations.** - +**Inputs** +- a: The Decimal struct to be shifted +- k: The number of positions to shift right +*/ shift_right :: proc(a: ^Decimal, k: uint) { r := 0 // read index w := 0 // write index @@ -304,7 +339,15 @@ _shift_left_offsets := [?]struct{delta: int, cutoff: string}{ {18, "173472347597680709441192448139190673828125"}, {19, "867361737988403547205962240695953369140625"}, } +/* +Shifts the decimal of the input value to the left by `k` places +WARNING: asserts `k < 61` + +**Inputs** +- a: The Decimal to be modified +- k: The number of places to shift the decimal to the left +*/ shift_left :: proc(a: ^Decimal, k: uint) #no_bounds_check { prefix_less :: #force_inline proc "contextless" (b: []byte, s: string) -> bool #no_bounds_check { for i in 0..=5) +*/ can_round_up :: proc(a: ^Decimal, nd: int) -> bool { if nd < 0 || nd >= a.count { return false } if a.digits[nd] == '5' && nd+1 == a.count { @@ -395,7 +452,13 @@ can_round_up :: proc(a: ^Decimal, nd: int) -> bool { return a.digits[nd] >= '5' } +/* +Rounds the Decimal at the given digit index +**Inputs** +- a: The Decimal to be modified +- nd: The digit index to round +*/ round :: proc(a: ^Decimal, nd: int) { if nd < 0 || nd >= a.count { return } if can_round_up(a, nd) { @@ -404,7 +467,13 @@ round :: proc(a: ^Decimal, nd: int) { round_down(a, nd) } } +/* +Rounds the Decimal up at the given digit index +**Inputs** +- a: The Decimal to be modified +- nd: The digit index to round up +*/ round_up :: proc(a: ^Decimal, nd: int) { if nd < 0 || nd >= a.count { return } @@ -421,15 +490,62 @@ round_up :: proc(a: ^Decimal, nd: int) { a.count = 1 a.decimal_point += 1 } +/* +Rounds down the decimal value to the specified number of decimal places +**Inputs** +- a: The Decimal value to be rounded down +- nd: The number of decimal places to round down to + +Example: + + import "core:fmt" + import "core:strconv" + + strconv_round_down_example :: proc { + d: decimal.Decimal + str := [64]u8{} + ok := decimal.set(&d, "123.456") + decimal.round_down(&d, 5) + fmt.println(decimal.decimal_to_string(str[:], &d)) + } + +Output: + + 123.45 + +*/ round_down :: proc(a: ^Decimal, nd: int) { if nd < 0 || nd >= a.count { return } a.count = nd trim(a) } +/* +Extracts the rounded integer part of a decimal value +**Inputs** +- a: A pointer to the Decimal value to extract the rounded integer part from -// Extract integer part, rounded appropriately. There are no guarantees about overflow. +WARNING: There are no guarantees about overflow. + +**Returns** The rounded integer part of the input decimal value + +Example: + + import "core:fmt" + import "core:strconv" + + strconv_rounded_integer_example :: proc { + d: decimal.Decimal + ok := decimal.set(&d, "123.456") + fmt.println(decimal.rounded_integer(&d)) + } + +Output: + + 123 + +*/ rounded_integer :: proc(a: ^Decimal) -> u64 { if a.decimal_point > 20 { return 0xffff_ffff_ffff_ffff diff --git a/core/strconv/generic_float.odin b/core/strconv/generic_float.odin index b612b8eb2..70febf832 100644 --- a/core/strconv/generic_float.odin +++ b/core/strconv/generic_float.odin @@ -20,7 +20,29 @@ _f16_info := Float_Info{10, 5, -15} _f32_info := Float_Info{23, 8, -127} _f64_info := Float_Info{52, 11, -1023} +/* +Converts a floating-point number to a string with the specified format and precision. +**Inputs** + +buf: A byte slice to store the resulting string +val: The floating-point value to be converted +fmt: The formatting byte, accepted values are 'e', 'E', 'f', 'F', 'g', 'G' +precision: The number of decimal places to round to +bit_size: The size of the floating-point number in bits, valid values are 16, 32, 64 + +Example: + + buf: [32]byte + val := 3.141592 + fmt := 'f' + precision := 2 + bit_size := 64 + result := strconv.generic_ftoa(buf[:], val, fmt, precision, bit_size) -> "3.14" + +**Returns** +- A byte slice containing the formatted string +*/ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) -> []byte { bits: u64 flt: ^Float_Info @@ -95,8 +117,20 @@ generic_ftoa :: proc(buf: []byte, val: f64, fmt: byte, precision, bit_size: int) return format_digits(buf, shortest, neg, digs, prec, fmt) } +/* +Converts a decimal floating-point number into a byte buffer with the given format +**Inputs** +- buf: The byte buffer to store the formatted number +- shortest: If true, generates the shortest representation of the number +- neg: If true, the number is negative +- digs: The decimal number to be formatted +- precision: The number of digits after the decimal point +- fmt: The format specifier (accepted values: 'f', 'F', 'e', 'E', 'g', 'G') +**Returns** +- A byte slice containing the formatted decimal floating-point number +*/ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slice, precision: int, fmt: byte) -> []byte { Buffer :: struct { b: []byte, @@ -217,7 +251,15 @@ format_digits :: proc(buf: []byte, shortest: bool, neg: bool, digs: Decimal_Slic } +/* +Rounds the given decimal number to its shortest representation, considering the provided floating-point format +**Inputs** +- d: The decimal number to round +- mant: The mantissa of the floating-point number +- exp: The exponent of the floating-point number +- flt: Pointer to the Float_Info structure containing information about the floating-point format +*/ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Info) { if mant == 0 { // If mantissa is zero, the number is zero d.count = 0 @@ -284,7 +326,17 @@ round_shortest :: proc(d: ^decimal.Decimal, mant: u64, exp: int, flt: ^Float_Inf } } +/* +Converts a decimal number to its floating-point representation with the given format and returns the resulting bits +**Inputs** +- d: Pointer to the decimal number to convert +- info: Pointer to the Float_Info structure containing information about the floating-point format + +**Returns** +- b: The bits representing the floating-point number +- overflow: A boolean indicating whether an overflow occurred during conversion +*/ @(private) decimal_to_float_bits :: proc(d: ^decimal.Decimal, info: ^Float_Info) -> (b: u64, overflow: bool) { end :: proc "contextless" (d: ^decimal.Decimal, mant: u64, exp: int, info: ^Float_Info) -> (bits: u64) { diff --git a/core/strconv/integers.odin b/core/strconv/integers.odin index f06e6e177..98a432ac5 100644 --- a/core/strconv/integers.odin +++ b/core/strconv/integers.odin @@ -9,7 +9,18 @@ Int_Flags :: bit_set[Int_Flag] MAX_BASE :: 32 digits := "0123456789abcdefghijklmnopqrstuvwxyz" +/* +Determines whether the given unsigned 64-bit integer is a negative value by interpreting it as a signed integer with the specified bit size. +**Inputs** +- x: The unsigned 64-bit integer to check for negativity +- is_signed: A boolean indicating if the input should be treated as a signed integer +- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64) + +**Returns** +- u: The absolute value of the input integer +- neg: A boolean indicating whether the input integer is negative +*/ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, neg: bool) { u = x if is_signed { @@ -36,7 +47,21 @@ is_integer_negative :: proc(x: u64, is_signed: bool, bit_size: int) -> (u: u64, } return } +/* +Appends the string representation of an integer to a buffer with specified base, flags, and digit set. +**Inputs** +- buf: The buffer to append the integer representation to +- x: The integer value to convert +- base: The base for the integer representation (2 <= base <= MAX_BASE) +- is_signed: A boolean indicating if the input should be treated as a signed integer +- bit_size: The bit size of the signed integer representation (8, 16, 32, or 64) +- digits: The digit set used for the integer representation +- flags: The Int_Flags bit set to control integer formatting + +**Returns** +- The string containing the integer representation appended to the buffer +*/ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { if base < 2 || base > MAX_BASE { panic("strconv: illegal base passed to append_bits") @@ -78,7 +103,18 @@ append_bits :: proc(buf: []byte, x: u64, base: int, is_signed: bool, bit_size: i copy(buf, out) return string(buf[0:len(out)]) } +/* +Determines whether the given unsigned 128-bit integer is a negative value by interpreting it as a signed integer with the specified bit size. +**Inputs** +- x: The unsigned 128-bit integer to check for negativity +- is_signed: A boolean indicating if the input should be treated as a signed integer +- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128) + +**Returns** +- u: The absolute value of the input integer +- neg: A boolean indicating whether the input integer is negative +*/ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: u128, neg: bool) { u = x if is_signed { @@ -109,9 +145,21 @@ is_integer_negative_128 :: proc(x: u128, is_signed: bool, bit_size: int) -> (u: } return } +/* +Appends the string representation of a 128-bit integer to a buffer with specified base, flags, and digit set. -// import "core:runtime" +**Inputs** +- buf: The buffer to append the integer representation to +- x: The 128-bit integer value to convert +- base: The base for the integer representation (2 <= base <= MAX_BASE) +- is_signed: A boolean indicating if the input should be treated as a signed integer +- bit_size: The bit size of the signed integer representation (8, 16, 32, 64, or 128) +- digits: The digit set used for the integer representation +- flags: The Int_Flags bit set to control integer formatting +**Returns** +- The string containing the integer representation appended to the buffer +*/ append_bits_128 :: proc(buf: []byte, x: u128, base: int, is_signed: bool, bit_size: int, digits: string, flags: Int_Flags) -> string { if base < 2 || base > MAX_BASE { panic("strconv: illegal base passed to append_bits") diff --git a/core/strconv/strconv.odin b/core/strconv/strconv.odin index e11ce471d..81b6267de 100644 --- a/core/strconv/strconv.odin +++ b/core/strconv/strconv.odin @@ -2,7 +2,19 @@ package strconv import "core:unicode/utf8" import "decimal" +/* +Parses a boolean value from the input string +**Inputs** +- s: The input string + - true: "1", "t", "T", "true", "TRUE", "True" + - false: "0", "f", "F", "false", "FALSE", "False" +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +**Returns** +- result: The parsed boolean value (default: false) +- ok: A boolean indicating whether the parsing was successful +*/ parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) { switch s { case "1", "t", "T", "true", "TRUE", "True": @@ -14,7 +26,14 @@ parse_bool :: proc(s: string, n: ^int = nil) -> (result: bool = false, ok: bool) } return } +/* +Finds the integer value of the given rune +**Inputs** +- r: The input rune to find the integer value of + +**Returns** The integer value of the given rune +*/ _digit_value :: proc(r: rune) -> int { ri := int(r) v: int = 16 @@ -25,16 +44,29 @@ _digit_value :: proc(r: rune) -> int { } return v } +/* +Parses an integer value from the input string in the given base, without a prefix -// Parses an integer value from a string, in the given base, without a prefix. -// -// Returns ok=false if no numeric value of the appropriate base could be found, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_i64_of_base("-1234eeee", 10); -// assert(n == -1234 && ok); -// ``` +**Inputs** +- str: The input string to parse the integer value from +- base: The base of the integer value to be parsed (must be between 1 and 16) +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +Example: + + parse_i64_of_base_example :: proc() { + n, ok := strconv.parse_i64_of_base("-1234e3", 10) + fmt.println(n, ok) + } + +Output: + + -1234 false + +**Returns** +- value: Parses an integer value from a string, in the given base, without a prefix. +- ok: ok=false if no numeric value of the appropriate base could be found, or if the input string contained more than just the number. +*/ parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok: bool) { assert(base <= 16, "base must be 1-16") @@ -80,19 +112,32 @@ parse_i64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i64, ok = len(s) == 0 return } +/* +Parses an integer value from the input string in base 10, unless there's a prefix -// Parses a integer value from a string, in base 10, unless there's a prefix. -// -// Returns ok=false if a valid integer could not be found, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_i64_maybe_prefixed("1234"); -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_i64_maybe_prefixed("0xeeee"); -// assert(n == 0xeeee && ok); -// ``` +**Inputs** +- str: The input string to parse the integer value from +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +Example: + + parse_i64_maybe_prefixed_example :: proc() { + n, ok := strconv.parse_i64_maybe_prefixed("1234") + fmt.println(n,ok) + + n, ok = strconv.parse_i64_maybe_prefixed("0xeeee") + fmt.println(n,ok) + } + +Output: + + 1234 true + 61166 true + +**Returns** +- value: The parsed integer value +- ok: ok=false if a valid integer could not be found, or if the input string contained more than just the number. +*/ parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: bool) { s := str defer if n != nil { n^ = len(str)-len(s) } @@ -146,22 +191,36 @@ parse_i64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i64, ok: ok = len(s) == 0 return } - +// parse_i64 :: proc{parse_i64_maybe_prefixed, parse_i64_of_base} +/* +Parses an unsigned 64-bit integer value from the input string without a prefix, using the specified base -// Parses an unsigned integer value from a string, in the given base, and -// without a prefix. -// -// Returns ok=false if no numeric value of the appropriate base could be found, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_u64_of_base("1234eeee", 10); -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_u64_of_base("5678eeee", 16); -// assert(n == 0x5678eeee && ok); -// ``` +**Inputs** +- str: The input string to parse +- base: The base of the number system to use for parsing + - Must be between 1 and 16 (inclusive) +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +Example: + + parse_u64_of_base_example :: proc() { + n, ok := strconv.parse_u64_of_base("1234e3", 10) + fmt.println(n,ok) + + n, ok = strconv.parse_u64_of_base("5678eee",16) + fmt.println(n,ok) + } + +Output: + + 1234 false + 90672878 true + +**Returns** +- value: The parsed uint64 value +- ok: A boolean indicating whether the parsing was successful +*/ parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok: bool) { assert(base <= 16, "base must be 1-16") s := str @@ -193,19 +252,35 @@ parse_u64_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u64, ok = len(s) == 0 return } +/* +Parses an unsigned 64-bit integer value from the input string, using the specified base or inferring the base from a prefix -// Parses an unsigned integer value from a string in base 10, unless there's a prefix. -// -// Returns ok=false if a valid integer could not be found, if the value was negative, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_u64_maybe_prefixed("1234"); -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_u64_maybe_prefixed("0xeeee"); -// assert(n == 0xeeee && ok); -// ``` +**Inputs** +- str: The input string to parse +- base: The base of the number system to use for parsing (default: 0) + - If base is 0, it will be inferred based on the prefix in the input string (e.g. '0x' for hexadecimal) + - If base is not 0, it will be used for parsing regardless of any prefix in the input string +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +Example: + + parse_u64_maybe_prefixed_example :: proc() { + n, ok := strconv.parse_u64_maybe_prefixed("1234") + fmt.println(n,ok) + + n, ok = strconv.parse_u64_maybe_prefixed("0xee") + fmt.println(n,ok) + } + +Output: + + 1234 true + 238 true + +**Returns** +- value: The parsed uint64 value +- ok: ok=false if a valid integer could not be found, if the value was negative, or if the input string contained more than just the number. +*/ parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: bool) { s := str defer if n != nil { n^ = len(str)-len(s) } @@ -248,26 +323,40 @@ parse_u64_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u64, ok: ok = len(s) == 0 return } - +// parse_u64 :: proc{parse_u64_maybe_prefixed, parse_u64_of_base} +/* +Parses a signed integer value from the input string, using the specified base or inferring the base from a prefix -// Parses an integer value from a string in the given base, or -// - if the string has a prefix (e.g: '0x') then that will determine the base; -// - otherwise, assumes base 10. -// -// Returns ok=false if no appropriate value could be found, or if the input string -// contained more than just the number. -// -// ``` -// n, ok := strconv.parse_int("1234"); // without prefix, inferred base 10 -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_int("ffff", 16); // without prefix, explicit base -// assert(n == 0xffff && ok); -// -// n, ok = strconv.parse_int("0xffff"); // with prefix and inferred base -// assert(n == 0xffff && ok); -// ``` +**Inputs** +- s: The input string to parse +- base: The base of the number system to use for parsing (default: 0) + - If base is 0, it will be inferred based on the prefix in the input string (e.g. '0x' for hexadecimal) + - If base is not 0, it will be used for parsing regardless of any prefix in the input string + +Example: + + parse_int_example :: proc() { + n, ok := strconv.parse_int("1234") // without prefix, inferred base 10 + fmt.println(n,ok) + + n, ok = strconv.parse_int("ffff", 16) // without prefix, explicit base + fmt.println(n,ok) + + n, ok = strconv.parse_int("0xffff") // with prefix and inferred base + fmt.println(n,ok) + } + +Output: + + 1234 true + 65535 true + 65535 true + +**Returns** +- value: The parsed int value +- ok: `false` if no appropriate value could be found, or if the input string contained more than just the number. +*/ parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) { v: i64 = --- switch base { @@ -277,27 +366,39 @@ parse_int :: proc(s: string, base := 0, n: ^int = nil) -> (value: int, ok: bool) value = int(v) return } +/* +Parses an unsigned integer value from the input string, using the specified base or inferring the base from a prefix +**Inputs** +- s: The input string to parse +- base: The base of the number system to use for parsing (default: 0, inferred) + - If base is 0, it will be inferred based on the prefix in the input string (e.g. '0x' for hexadecimal) + - If base is not 0, it will be used for parsing regardless of any prefix in the input string -// Parses an unsigned integer value from a string in the given base, or -// - if the string has a prefix (e.g: '0x') then that will determine the base; -// - otherwise, assumes base 10. -// -// Returns ok=false if: -// - no appropriate value could be found; or -// - the value was negative. -// - the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_uint("1234"); // without prefix, inferred base 10 -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_uint("ffff", 16); // without prefix, explicit base -// assert(n == 0xffff && ok); -// -// n, ok = strconv.parse_uint("0xffff"); // with prefix and inferred base -// assert(n == 0xffff && ok); -// ``` +Example: + + parse_uint_example :: proc() { + n, ok := strconv.parse_uint("1234") // without prefix, inferred base 10 + fmt.println(n,ok) + + n, ok = strconv.parse_uint("ffff", 16) // without prefix, explicit base + fmt.println(n,ok) + + n, ok = strconv.parse_uint("0xffff") // with prefix and inferred base + fmt.println(n,ok) + } + +Output: + + 1234 true + 65535 true + 65535 true + +**Returns** + +value: The parsed uint value +ok: `false` if no appropriate value could be found; the value was negative; he input string contained more than just the number +*/ parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: bool) { v: u64 = --- switch base { @@ -307,17 +408,29 @@ parse_uint :: proc(s: string, base := 0, n: ^int = nil) -> (value: uint, ok: boo value = uint(v) return } +/* +Parses an integer value from a string in the given base, without any prefix +**Inputs** +- str: The input string containing the integer value +- base: The base (radix) to use for parsing the integer (1-16) +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) -// Parses an integer value from a string, in the given base, without a prefix. -// -// Returns ok=false if no numeric value of the appropriate base could be found, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_i128_of_base("-1234eeee", 10); -// assert(n == -1234 && ok); -// ``` +Example: + + parse_i128_of_base_example :: proc() { + n, ok := strconv.parse_i128_of_base("-1234eeee", 10) + fmt.println(n,ok) + } + +Output: + + -1234 false + +**Returns** +- value: The parsed i128 value +- ok: false if no numeric value of the appropriate base could be found, or if the input string contained more than just the number. +*/ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i128, ok: bool) { assert(base <= 16, "base must be 1-16") @@ -361,19 +474,32 @@ parse_i128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: i12 ok = len(s) == 0 return } +/* +Parses an integer value from a string in base 10, unless there's a prefix -// Parses a integer value from a string, in base 10, unless there's a prefix. -// -// Returns ok=false if a valid integer could not be found, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_i128_maybe_prefixed("1234"); -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_i128_maybe_prefixed("0xeeee"); -// assert(n == 0xeeee && ok); -// ``` +**Inputs** +- str: The input string containing the integer value +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +Example: + + parse_i128_maybe_prefixed_example :: proc() { + n, ok := strconv.parse_i128_maybe_prefixed("1234") + fmt.println(n, ok) + + n, ok = strconv.parse_i128_maybe_prefixed("0xeeee") + fmt.println(n, ok) + } + +Output: + + 1234 true + 61166 true + +**Returns** +- value: The parsed i128 value +- ok: `false` if a valid integer could not be found, or if the input string contained more than just the number. +*/ parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, ok: bool) { s := str defer if n != nil { n^ = len(str)-len(s) } @@ -427,22 +553,35 @@ parse_i128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: i128, o ok = len(s) == 0 return } - +// parse_i128 :: proc{parse_i128_maybe_prefixed, parse_i128_of_base} +/* +Parses an unsigned integer value from a string in the given base, without any prefix -// Parses an unsigned integer value from a string, in the given base, and -// without a prefix. -// -// Returns ok=false if no numeric value of the appropriate base could be found, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_u128_of_base("1234eeee", 10); -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_u128_of_base("5678eeee", 16); -// assert(n == 0x5678eeee && ok); -// ``` +**Inputs** +- str: The input string containing the integer value +- base: The base (radix) to use for parsing the integer (1-16) +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +Example: + + parse_u128_of_base_example :: proc() { + n, ok := strconv.parse_u128_of_base("1234eeee", 10) + fmt.println(n, ok) + + n, ok = strconv.parse_u128_of_base("5678eeee", 16) + fmt.println(n, ok) + } + +Output: + + 1234 false + 1450766062 true + +**Returns** +- value: The parsed u128 value +- ok: `false` if no numeric value of the appropriate base could be found, or if the input string contained more than just the number. +*/ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u128, ok: bool) { assert(base <= 16, "base must be 1-16") s := str @@ -474,19 +613,32 @@ parse_u128_of_base :: proc(str: string, base: int, n: ^int = nil) -> (value: u12 ok = len(s) == 0 return } +/* +Parses an unsigned integer value from a string in base 10, unless there's a prefix -// Parses an unsigned integer value from a string in base 10, unless there's a prefix. -// -// Returns ok=false if a valid integer could not be found, if the value was negative, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_u128_maybe_prefixed("1234"); -// assert(n == 1234 && ok); -// -// n, ok = strconv.parse_u128_maybe_prefixed("0xeeee"); -// assert(n == 0xeeee && ok); -// ``` +**Inputs** +- str: The input string containing the integer value +- n: An optional pointer to an int to store the length of the parsed substring (default: nil) + +Example: + + parse_u128_maybe_prefixed_example :: proc() { + n, ok := strconv.parse_u128_maybe_prefixed("1234") + fmt.println(n, ok) + + n, ok = strconv.parse_u128_maybe_prefixed("5678eeee") + fmt.println(n, ok) + } + +Output: + + 1234 true + 5678 false + +**Returns** +- value: The parsed u128 value +- ok: false if a valid integer could not be found, if the value was negative, or if the input string contained more than just the number. +*/ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, ok: bool) { s := str defer if n != nil { n^ = len(str)-len(s) } @@ -529,34 +681,76 @@ parse_u128_maybe_prefixed :: proc(str: string, n: ^int = nil) -> (value: u128, o ok = len(s) == 0 return } - +// parse_u128 :: proc{parse_u128_maybe_prefixed, parse_u128_of_base} +/* +Converts a byte to lowercase +**Inputs** +- ch: A byte character to be converted to lowercase. +**Returns** +- A lowercase byte character. +*/ @(private) lower :: #force_inline proc "contextless" (ch: byte) -> byte { return ('a' - 'A') | ch } +/* +Parses a 32-bit floating point number from a string +**Inputs** +- s: The input string containing a 32-bit floating point number. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). +Example: -// Parses a 32-bit floating point number from a string. -// -// Returns ok=false if a base 10 float could not be found, -// or if the input string contained more than just the number. -// -// ``` -// n, ok := strconv.parse_f32("12.34eee"); -// assert(n == 12.34 && ok); -// -// n, ok = strconv.parse_f32("12.34"); -// assert(n == 12.34 && ok); -// ``` + parse_f32_example :: proc() { + n, ok := strconv.parse_f32("1234eee") + fmt.println(n, ok) + + n, ok = strconv.parse_f32("5678e2") + fmt.println(n, ok) + } + +Output: + + 0.000 false + 567800.000 true + +**Returns** +- value: The parsed 32-bit floating point number. +- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number. +*/ parse_f32 :: proc(s: string, n: ^int = nil) -> (value: f32, ok: bool) { v: f64 = --- v, ok = parse_f64(s, n) return f32(v), ok } +/* +Parses a 64-bit floating point number from a string +**Inputs** +- str: The input string containing a 64-bit floating point number. +- n: An optional pointer to an int to store the length of the parsed substring (default: nil). +Example: + + parse_f64_example :: proc() { + n, ok := strconv.parse_f64("1234eee") + fmt.println(n, ok) + + n, ok = strconv.parse_f64("5678e2") + fmt.println(n, ok) + } + +Output: + + 0.000 false + 567800.000 true + +**Returns** +- value: The parsed 64-bit floating point number. +- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number. +*/ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) { nr: int value, nr, ok = parse_f64_prefix(str) @@ -566,40 +760,65 @@ parse_f64 :: proc(str: string, n: ^int = nil) -> (value: f64, ok: bool) { if n != nil { n^ = nr } return } +/* +Parses a 32-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful +**Inputs** +- str: The input string containing a 32-bit floating point number. -// Parses a 32-bit floating point number from a string. -// -// Returns ok=false if a base 10 float could not be found, -// or if the input string contained more than just the number. -// -// ``` -// n, _, ok := strconv.parse_f32("12.34eee"); -// assert(n == 12.34 && ok); -// -// n, _, ok = strconv.parse_f32("12.34"); -// assert(n == 12.34 && ok); -// ``` +Example: + + parse_f32_prefix_example :: proc() { + n, _, ok := strconv.parse_f32_prefix("1234eee") + fmt.println(n, ok) + + n, _, ok = strconv.parse_f32_prefix("5678e2") + fmt.println(n, ok) + } + +Output: + + 0.000 false + 567800.000 true + + +**Returns** +- value: The parsed 32-bit floating point number. +- nr: The length of the parsed substring. +- ok: A boolean indicating whether the parsing was successful. +*/ parse_f32_prefix :: proc(str: string) -> (value: f32, nr: int, ok: bool) { f: f64 f, nr, ok = parse_f64_prefix(str) value = f32(f) return } +/* +Parses a 64-bit floating point number from a string and returns the parsed number, the length of the parsed substring, and a boolean indicating whether the parsing was successful +**Inputs** +- str: The input string containing a 64-bit floating point number. -// Parses a 64-bit floating point number from a string. -// -// Returns ok=false if a base 10 float could not be found, -// or if the input string contained more than just the number. -// -// ``` -// n, _, ok := strconv.parse_f32("12.34eee"); -// assert(n == 12.34 && ok); -// -// n, _, ok = strconv.parse_f32("12.34"); -// assert(n == 12.34 && ok); -// ``` +Example: + + parse_f64_prefix_example :: proc() { + n, _, ok := strconv.parse_f64_prefix("12.34eee") + fmt.println(n, ok) + + n, _, ok = strconv.parse_f64_prefix("12.34e2") + fmt.println(n, ok) + } + +Output: + + 0.000 false + 1234.000 true + +**Returns** +- value: The parsed 64-bit floating point number. +- nr: The length of the parsed substring. +- ok: `false` if a base 10 float could not be found, or if the input string contained more than just the number. +*/ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { common_prefix_len_ignore_case :: proc "contextless" (s, prefix: string) -> int { n := len(prefix) @@ -877,8 +1096,28 @@ parse_f64_prefix :: proc(str: string) -> (value: f64, nr: int, ok: bool) { ok = !overflow return } +/* +Appends a boolean value as a string to the given buffer +**Inputs** +- buf: The buffer to append the boolean value to +- b: The boolean value to be appended +Example: + + append_bool_example :: proc() { + buf: [4]byte + result := strconv.append_bool(buf[:], true) + fmt.println(result, buf) + } + +Output: + + true [116, 114, 117, 101, 0, 0] + +**Returns** +- The resulting string after appending the boolean value +*/ append_bool :: proc(buf: []byte, b: bool) -> string { n := 0 if b { @@ -888,32 +1127,182 @@ append_bool :: proc(buf: []byte, b: bool) -> string { } return string(buf[:n]) } +/* +Appends an unsigned integer value as a string to the given buffer with the specified base +**Inputs** +- buf: The buffer to append the unsigned integer value to +- u: The unsigned integer value to be appended +- base: The base to use for converting the integer value + +Example: + + append_uint_example :: proc() { + buf: [4]byte + result := strconv.append_uint(buf[:], 42, 16) + fmt.println(result, buf) + } + +Output: + + 2a [50, 97, 0, 0] + +**Returns** +- The resulting string after appending the unsigned integer value +*/ append_uint :: proc(buf: []byte, u: u64, base: int) -> string { return append_bits(buf, u, base, false, 8*size_of(uint), digits, nil) } +/* +Appends a signed integer value as a string to the given buffer with the specified base + +**Inputs** +- buf: The buffer to append the signed integer value to +- i: The signed integer value to be appended +- base: The base to use for converting the integer value + +Example: + + append_int_example :: proc() { + buf: [4]byte + result := strconv.append_int(buf[:], -42, 10) + fmt.println(result, buf) + } + +Output: + + -42 [45, 52, 50, 0] + +**Returns** +- The resulting string after appending the signed integer value +*/ append_int :: proc(buf: []byte, i: i64, base: int) -> string { return append_bits(buf, u64(i), base, true, 8*size_of(int), digits, nil) } +/* +Converts an integer value to a string and stores it in the given buffer +**Inputs** +- buf: The buffer to store the resulting string +- i: The integer value to be converted + +Example: + + itoa_example :: proc() { + buf: [4]byte + result := strconv.itoa(buf[:], 42) + fmt.println(result, buf) // "42" + } + +Output: + + 42 [52, 50, 0, 0] + +**Returns** +- The resulting string after converting the integer value +*/ itoa :: proc(buf: []byte, i: int) -> string { return append_int(buf, i64(i), 10) } +/* +Converts a string to an integer value + +**Inputs** +- s: The string to be converted + +Example: + + atoi_example :: proc() { + fmt.println(strconv.atoi("42")) + } + +Output: + + 42 + +**Returns** +- The resulting integer value +*/ atoi :: proc(s: string) -> int { v, _ := parse_int(s) return v } +/* +Converts a string to a float64 value + +**Inputs** +- s: The string to be converted + +Example: + + atof_example :: proc() { + fmt.println(strconv.atof("3.14")) + } + +Output: + + 3.140 + +**Returns** +- The resulting float64 value after converting the string +*/ atof :: proc(s: string) -> f64 { v, _ := parse_f64(s) return v } - +// Alias to `append_float` ftoa :: append_float +/* +Appends a float64 value as a string to the given buffer with the specified format and precision + +**Inputs** +- buf: The buffer to append the float64 value to +- f: The float64 value to be appended +- fmt: The byte specifying the format to use for the conversion +- prec: The precision to use for the conversion +- bit_size: The size of the float in bits (32 or 64) + +Example: + + append_float_example :: proc() { + buf: [8]byte + result := strconv.append_float(buf[:], 3.14159, 'f', 2, 64) + fmt.println(result, buf) + } + +Output: + + +3.14 [43, 51, 46, 49, 52, 0, 0, 0] + +**Returns** +- The resulting string after appending the float +*/ append_float :: proc(buf: []byte, f: f64, fmt: byte, prec, bit_size: int) -> string { return string(generic_ftoa(buf, f, fmt, prec, bit_size)) } +/* +Appends a quoted string representation of the input string to a given byte slice and returns the result as a string +**Inputs** +- buf: The byte slice to which the quoted string will be appended +- str: The input string to be quoted +Example: + + quote_example :: proc() { + buf: [20]byte + result := strconv.quote(buf[:], "hello") + fmt.println(result, buf) + } + +Output: + + !! ISSUE !! NOT EXPECTED -- "\"hello\"" was expected + "'h''e''l''l''o'" [34, 39, 104, 39, 39, 101, 39, 39, 108, 39, 39, 108, 39, 39, 111, 39, 34, 0, 0, 0] + +**Returns** +- The resulting string after appending the quoted string representation +*/ quote :: proc(buf: []byte, str: string) -> string { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { if i^ >= len(buf) { @@ -951,7 +1340,28 @@ quote :: proc(buf: []byte, str: string) -> string { write_byte(buf, &i, c) return string(buf[:i]) } +/* +Appends a quoted rune representation of the input rune to a given byte slice and returns the result as a string +**Inputs** +- buf: The byte slice to which the quoted rune will be appended +- r: The input rune to be quoted + +Example: + + quote_rune_example :: proc() { + buf: [4]byte + result := strconv.quote_rune(buf[:], 'A') + fmt.println(result, buf) + } + +Output: + + 'A' [39, 65, 39, 0] + +**Returns** +- The resulting string after appending the quoted rune representation +*/ quote_rune :: proc(buf: []byte, r: rune) -> string { write_byte :: proc(buf: []byte, i: ^int, bytes: ..byte) { if i^ < len(buf) { @@ -1007,10 +1417,33 @@ quote_rune :: proc(buf: []byte, r: rune) -> string { return string(buf[:i]) } +/* +Unquotes a single character from the input string, considering the given quote character +**Inputs** +- str: The input string containing the character to unquote +- quote: The quote character to consider (e.g., '"') +Example: + unquote_char_example :: proc() { + src:="\'The\' raven" + r, multiple_bytes, tail_string, success := strconv.unquote_char(src,'\'') + fmt.println("Source:", src) + fmt.printf("r: <%v>, multiple_bytes:%v, tail_string:<%s>, success:%v\n",r, multiple_bytes, tail_string, success) + } +Output: + + Source: 'The' raven + r: <'>, multiple_bytes:false, tail_string:, success:true + +**Returns** +- r: The unquoted rune +- multiple_bytes: A boolean indicating if the rune has multiple bytes +- tail_string: The remaining portion of the input string after unquoting the character +- success: A boolean indicating whether the unquoting was successful +*/ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool, tail_string: string, success: bool) { hex_to_int :: proc(c: byte) -> int { switch c { @@ -1105,7 +1538,52 @@ unquote_char :: proc(str: string, quote: byte) -> (r: rune, multiple_bytes: bool tail_string = s return } +/* +Unquotes the input string considering any type of quote character and returns the unquoted string +**Inputs** +- lit: The input string to unquote +- allocator: (default: context.allocator) + +WARNING: This procedure gives unexpected results if the quotes are not the first and last characters. + +Example: + + unquote_string_example :: proc() { + src:="\"The raven Huginn is black.\"" + s, allocated, ok := strconv.unquote_string(src) + fmt.println(src) + fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n\n", s, allocated, ok) + + src="\'The raven Huginn\' is black." + s, allocated, ok = strconv.unquote_string(src) + fmt.println(src) + fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n\n", s, allocated, ok) + + src="The raven \"Huginn\" is black." + s, allocated, ok = strconv.unquote_string(src) // Will produce undesireable results + fmt.println(src) + fmt.printf("Unquoted: <%s>, alloc:%v, ok:%v\n", s, allocated, ok) + } + +Output: + + "The raven Huginn is black." + Unquoted: , alloc:false, ok:true + + 'The raven Huginn' is black. + Unquoted: , alloc:false, ok:true + + Source: The raven `Huginn` is black. + Unquoted: , alloc:false, ok:true + +**Returns** +- res: The resulting unquoted string +- allocated: A boolean indicating if the resulting string was allocated using the provided allocator +- success: A boolean indicating whether the unquoting was successful + +NOTE: If unquoting is unsuccessful, the allocated memory for the result will be freed. +*/ unquote_string :: proc(lit: string, allocator := context.allocator) -> (res: string, allocated, success: bool) { contains_rune :: proc(s: string, r: rune) -> int { for c, offset in s { diff --git a/core/strings/strings.odin b/core/strings/strings.odin index 3c55374b7..dff68982d 100644 --- a/core/strings/strings.odin +++ b/core/strings/strings.odin @@ -1081,7 +1081,6 @@ split_by_byte_iterator :: proc(s: ^string, sep: u8) -> (res: string, ok: bool) { } /* Splits the input string by the separator string in an iterator fashion. -Destructively consumes the original string until the end. Inputs: - s: Pointer to the input string, which is modified during the search. @@ -1116,7 +1115,6 @@ split_iterator :: proc(s: ^string, sep: string) -> (string, bool) { } /* Splits the input string after every separator string in an iterator fashion. -Destructively consumes the original string until the end. Inputs: - s: Pointer to the input string, which is modified during the search. diff --git a/core/sys/windows/kernel32.odin b/core/sys/windows/kernel32.odin index 97334a837..a6897f164 100644 --- a/core/sys/windows/kernel32.odin +++ b/core/sys/windows/kernel32.odin @@ -409,7 +409,7 @@ foreign kernel32 { // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-setfilecompletionnotificationmodes) SetFileCompletionNotificationModes :: proc(FileHandle: HANDLE, Flags: u8) -> BOOL --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-createiocompletionport) - CreateIoCompletionPort :: proc(FileHandle: HANDLE, ExistingCompletionPort: HANDLE, CompletionKey: uintptr, NumberOfConcurrentThreads: DWORD) -> HANDLE --- + CreateIoCompletionPort :: proc(FileHandle: HANDLE, ExistingCompletionPort: HANDLE, CompletionKey: ^uintptr, NumberOfConcurrentThreads: DWORD) -> HANDLE --- //[MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatus) GetQueuedCompletionStatus :: proc(CompletionPort: HANDLE, lpNumberOfBytesTransferred: ^DWORD, lpCompletionKey: uintptr, lpOverlapped: ^^OVERLAPPED, dwMilliseconds: DWORD) -> BOOL --- // [MS-Docs](https://learn.microsoft.com/en-us/windows/win32/api/ioapiset/nf-ioapiset-getqueuedcompletionstatusex)