fix Object.free (#982)

* lib/std/collections: add HashMap.@each_entry()
* lib/std/json: fix Object.free() when object is a map
* lib/std/collections: fix allocator use in Object.{set,set_at,append}
* lib/std: add char.from_hex
* lib/std/collections: print arrays and objects compactly
* lib/std/io: fix Formatter.vprintf result
* lib/std/io/stream: rename module for ByteBuffer
* lib/std/io/stream: make Scanner a Stream reader
* lib/std/io: make std{in,err,out} return File* if no libc
This commit is contained in:
Pierre Curto
2023-09-12 13:49:52 +02:00
committed by GitHub
parent 37bb16cca1
commit d61482dffc
11 changed files with 141 additions and 52 deletions

View File

@@ -53,6 +53,10 @@ fn bool char.is_blank(char c) => is_blank_m(c);
fn bool char.is_cntrl(char c) => is_cntrl_m(c);
fn char char.to_lower(char c) => (char)to_lower_m(c);
fn char char.to_upper(char c) => (char)to_upper_m(c);
/**
* @require c.is_xdigit()
**/
fn char char.from_hex(char c) => c.is_digit() ? c - '0' : 10 + (c | 0x20) - 'a';
fn bool uint.in_range(uint c, uint start, uint len) => in_range_m(c, start, len);
fn bool uint.is_lower(uint c) => is_lower_m(c);

View File

@@ -209,6 +209,13 @@ fn Key[] HashMap.key_list(&map, Allocator* using = mem::heap())
}
macro HashMap.@each(map; @body(key, value))
{
map.@each_entry(; Entry* entry) {
@body(entry.key, entry.value);
};
}
macro HashMap.@each_entry(map; @body(entry))
{
if (map.count)
{
@@ -216,7 +223,7 @@ macro HashMap.@each(map; @body(key, value))
{
while (entry)
{
@body(entry.key, entry.value);
@body(entry);
entry = entry.next;
}
}

View File

@@ -43,10 +43,10 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
usz n = formatter.printf("[")!;
foreach (i, ol : self.array)
{
n += formatter.printf(i == 0 ? " " : ", ")!;
if (i > 0) n += formatter.printf(",")!;
n += ol.to_format(formatter)!;
}
n += formatter.printf(" ]")!;
n += formatter.printf("]")!;
return n;
case ObjectInternalMap:
usz n = formatter.printf("{")!;
@@ -54,12 +54,12 @@ fn usz! Object.to_format(&self, Formatter* formatter) @dynamic
{
foreach (i, key : self.map.key_tlist())
{
n += formatter.printf(i == 0 ? " " : ", ")!;
n += formatter.printf(`"%s": `, key)!;
if (i > 0) n += formatter.printf(",")!;
n += formatter.printf(`"%s":`, key)!;
n += self.map.get(key).to_format(formatter)!;
}
};
n += formatter.printf(" }")!;
n += formatter.printf("}")!;
return n;
default:
switch (self.type.kindof)
@@ -104,7 +104,7 @@ macro Object* new_enum(e, Allocator* using = mem::heap())
return o;
}
fn Object* new_float(double f, Allocator* using = mem::current_allocator())
fn Object* new_float(double f, Allocator* using = mem::heap())
{
Object* o = malloc(Object, .using = using);
*o = { .f = f, .allocator = using, .type = double.typeid };
@@ -139,14 +139,9 @@ fn void Object.free(&self)
}
self.array.free();
case ObjectInternalMap:
@pool()
{
foreach (key : self.map.key_tlist())
{
(void)self.map.get(key).free();
free(key, .using = self.allocator);
}
self.map.free();
self.map.@each_entry(; ObjectInternalMapEntry* entry) {
free(entry.key, .using = self.allocator);
entry.value.free();
};
default:
break;
@@ -204,17 +199,17 @@ fn void Object.set_object(&self, String key, Object* new_object) @private
self.map.set(key.copy(self.map.allocator), new_object);
}
macro Object* object_from_value(value) @private
macro Object* Object.object_from_value(&self, value) @private
{
var $Type = $typeof(value);
$switch
$case types::is_int($Type):
return new_int(value);
return new_int(value, self.allocator);
$case types::is_float($Type):
return new_float(value);
return new_float(value, self.allocator);
$case $Type.typeid == String.typeid:
return new_string(value);
return new_string(value, self.allocator);
$case $Type.typeid == bool.typeid:
return new_bool(value);
$case $Type.typeid == Object*.typeid:
@@ -223,7 +218,7 @@ macro Object* object_from_value(value) @private
if (value != null) return CastResult.TYPE_MISMATCH?;
return &NULL_OBJECT;
$case $checks(String s = value):
return new_string(value);
return new_string(value, self.allocator);
$default:
$error "Unsupported object type.";
$endswitch
@@ -232,7 +227,7 @@ macro Object* object_from_value(value) @private
macro Object* Object.set(&self, String key, value)
{
Object* val = object_from_value(value);
Object* val = self.object_from_value(value);
self.set_object(key, val);
return val;
}
@@ -242,7 +237,7 @@ macro Object* Object.set(&self, String key, value)
**/
macro Object* Object.set_at(&self, usz index, String key, value)
{
Object* val = object_from_value(value);
Object* val = self.object_from_value(value);
self.set_object_at(key, index, val);
return val;
}
@@ -253,7 +248,7 @@ macro Object* Object.set_at(&self, usz index, String key, value)
**/
macro Object* Object.append(&self, value)
{
Object* val = object_from_value(value);
Object* val = self.object_from_value(value);
self.append_object(val);
return val;
}
@@ -477,7 +472,7 @@ fn double! Object.get_float_at(&self, usz index)
fn Object* Object.get_or_create_obj(&self, String key)
{
if (try obj = self.get(key) && !obj.is_null()) return obj;
Object* container = new_obj();
Object* container = new_obj(self.allocator);
self.set(key, container);
return container;
}

View File

@@ -293,6 +293,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
// use null output function
self.out_fn = &out_null_fn;
}
usz total_len;
usz format_len = format.len;
usz variant_index = 0;
for (usz i = 0; i < format_len; i++)
@@ -302,7 +303,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
if (c != '%')
{
// no
self.out(c)!;
total_len += self.out(c)!;
continue;
}
i++;
@@ -310,7 +311,7 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
c = format[i];
if (c == '%')
{
self.out(c)!;
total_len += self.out(c)!;
continue;
}
// evaluate flags
@@ -377,42 +378,43 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
self.flags.uppercase = true;
nextcase;
case 'a':
self.atoa(float_from_any(current)!!)!;
total_len += self.atoa(float_from_any(current)!!)!;
continue;
case 'F' :
self.flags.uppercase = true;
nextcase;
case 'f':
self.ftoa(float_from_any(current)!!)!;
total_len += self.ftoa(float_from_any(current)!!)!;
continue;
case 'E':
self.flags.uppercase = true;
nextcase;
case 'e':
self.etoa(float_from_any(current)!!)!;
total_len += self.etoa(float_from_any(current)!!)!;
continue;
case 'G':
self.flags.uppercase = true;
nextcase;
case 'g':
self.gtoa(float_from_any(current)!!)!;
total_len += self.gtoa(float_from_any(current)!!)!;
continue;
case 'c':
self.out_char(current)!;
total_len += self.out_char(current)!;
continue;
case 's':
if (self.flags.left)
{
usz len = self.out_str(current)!;
self.pad(' ', self.width, len)!;
total_len += len;
total_len += self.pad(' ', self.width, len)!;
continue;
}
OutputFn out_fn = self.out_fn;
self.out_fn = (OutputFn)&out_null_fn;
usz len = self.out_str(current)!;
self.out_fn = out_fn;
self.pad(' ', self.width, len)!;
self.out_str(current)!;
total_len += self.pad(' ', self.width, len)!;
total_len += self.out_str(current)!;
continue;
case 'p':
self.flags.zeropad = true;
@@ -432,13 +434,13 @@ fn usz! Formatter.vprintf(&self, String format, any[] anys)
bool is_neg;
uint128 v = int_from_any(current, &is_neg)!!;
self.ntoa(v, is_neg, base)!;
total_len += self.ntoa(v, is_neg, base)!;
}
// termination
// out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
// return written chars without terminating \0
return self.idx;
return total_len;
}

View File

@@ -141,17 +141,17 @@ fn void putchar(char c) @inline
(void)stdout_file.putc(c);
}
fn File stdout()
fn File* stdout()
{
return stdout_file;
return &stdout_file;
}
fn File stderr()
fn File* stderr()
{
return stderr_file;
return &stderr_file;
}
fn File stdin()
fn File* stdin()
{
return stdin_file;
return &stdin_file;
}

View File

@@ -30,9 +30,19 @@ fn ByteBuffer*! ByteBuffer.tinit(&self, usz max_read, usz initial_capacity = 16)
return self.init(max_read, initial_capacity, mem::temp())!;
}
/**
* @require buf.len > 0
* @require self.bytes.len == 0 "Buffer already initialized."
**/
fn ByteBuffer*! ByteBuffer.init_with_buffer(&self, char[] buf)
{
*self = { .stream.fns = &BYTEBUFFER_INTERFACE, .max_read = buf.len, .bytes = buf };
return self;
}
fn void! ByteBuffer.free(&self)
{
self.allocator.free(self.bytes)!;
if (self.allocator) self.allocator.free(self.bytes)!;
*self = {};
}
@@ -104,6 +114,23 @@ fn void! ByteBuffer.pushback_byte(&self)
self.has_last = false;
}
fn void! ByteBuffer.seek(&self, isz offset, Seek seek)
{
switch (seek)
{
case SET:
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
self.read_idx = offset;
case CURSOR:
if ((offset < 0 && self.read_idx < -offset) ||
(offset > 0 && self.read_idx + offset > self.write_idx)) return IoError.INVALID_POSITION?;
self.read_idx += offset;
case END:
if (offset < 0 || offset > self.write_idx) return IoError.INVALID_POSITION?;
self.read_idx = self.write_idx - offset;
}
}
fn usz! ByteBuffer.available(&self) @inline
{
return self.write_idx - self.read_idx;

View File

@@ -1,4 +1,4 @@
module std::io::stream;
module std::io;
struct LimitReader
{

View File

@@ -2,7 +2,8 @@ module std::io;
struct Scanner
{
Stream* stream;
inline Stream stream;
Stream* wrapped_stream;
char[] buf;
usz pattern_idx;
usz read_idx;
@@ -20,9 +21,14 @@ struct Scanner
**/
fn void Scanner.init(&self, Stream* stream, char[] buffer)
{
*self = { .stream = stream, .buf = buffer };
*self = { .stream.fns = &SCANNER_INTERFACE, .wrapped_stream = stream, .buf = buffer };
}
const StreamInterface SCANNER_INTERFACE = {
.read_fn = (ReadStreamFn)&Scanner.read,
.read_byte_fn = (ReadByteStreamFn)&Scanner.read_byte,
};
/**
* Return and clear any remaining unscanned data.
**/
@@ -85,7 +91,7 @@ macro usz! Scanner.find(&self, buf, pattern) @private
macro usz! Scanner.refill(&self, buf) @private
{
usz! n = self.stream.read(buf);
usz! n = self.wrapped_stream.read(buf);
if (catch err = n)
{
case IoError.EOF:
@@ -94,4 +100,27 @@ macro usz! Scanner.refill(&self, buf) @private
return err?;
}
return n;
}
fn usz! Scanner.read(&self, char[] bytes)
{
usz n;
if (self.pattern_idx < self.read_idx)
{
n = min(bytes.len, self.read_idx - self.pattern_idx);
bytes[:n] = self.buf[self.pattern_idx:n];
self.pattern_idx += n;
bytes = bytes[n..];
}
n += self.wrapped_stream.read(bytes)!;
return n;
}
fn char! Scanner.read_byte(&self)
{
if (self.pattern_idx < self.read_idx)
{
return self.buf[self.pattern_idx++];
}
return self.wrapped_stream.read_byte();
}

View File

@@ -8,6 +8,7 @@ fn void! simple_test()
ByteReader reader;
reader.init(`{ "b": 123, "c": [ { "d": 66 }, null, "hello", false, { "id": "xyz" } ] }`);
Object* o = json::parse(&reader)!;
defer o.free();
assert(o.get_int("b")! == 123);
assert(o.get("c").get_len()! == 5);
assert(o.get("c").get_at(0).get_int("d")! == 66);
@@ -16,3 +17,11 @@ fn void! simple_test()
assert(o.get("c").get_bool_at(3)! == false);
assert(o.get("c").get_at(4).get_string("id")! == "xyz");
}
fn void! simple_test2()
{
ByteReader reader;
reader.init(`{"jsonrpc":"2.0","id":null,"method":"initialize"}`);
Object* o = json::parse(&reader)!;
defer o.free();
}

View File

@@ -1,7 +1,7 @@
module bytebuffer_test @test;
module std::io::bytebuffer @test;
import std::io;
fn void! write_read_test()
fn void! write_read()
{
ByteBuffer buffer;
buffer.init(0)!;

View File

@@ -1,6 +1,5 @@
module scanner_test @test;
module std::io @test;
import std::collections::list;
import std::io;
def Results = List(<String>);
@@ -11,7 +10,7 @@ struct ScanTest
String left_over;
}
fn void! test_scanner()
fn void! scanner()
{
ScanTest[] tcases = {
{"aa,,bb", {"aa"}, "bb"},
@@ -48,4 +47,21 @@ fn void! test_scanner()
String left_over = (String)fl;
assert(left_over == tc.left_over, "%s -> %s", tc.in, left_over);
}
}
fn void! scanner_as_reader()
{
ByteReader br;
br.init("Lorem ipsum sit.");
Scanner sc;
char[8] buffer;
sc.init(&br, buffer[..]);
sc.scan(" ")!;
char[16] res;
usz n = sc.read(&res)!;
String str = (String)res[:n];
assert(str == "ipsum sit.", "got '%s'; want 'ipsum sit.'", str);
}