package core.io
+// Reader is a buffered reader over a Stream.
+// If you do not need a buffered reader, simply
+// use the stream_read functions to read from the
+// stream. Because Reader is buffered, there are
+// several things it can do that a non-buffered
+// reader could not, like 'read the next line',
+// or 'read until a period'.
+
use core {memory, math, array, iter}
Reader :: struct {
- stream : ^Stream;
+ stream: ^Stream;
buffer: [] u8;
buffer_allocator: Allocator;
start, end: u32; // The start and ending positions of the edges of the buffer "window".
-
last_byte: i32;
error: Error;
-
- done : bool; // If an .EOF was reached.
-
- // If a .ReadPending was reached, still work with the data
- // that is available. Only set this if you know that the data
- // will always be available.
- greedy : bool;
-
- // A very special flag that should probably only be used on
- // standard input (hence the name). This flag says that if there
- // is ANY data left in the buffer, do not try to fill the buffer
- // with a call to stream_read. This will produce incorrect results
- // for things like read_line, but will prevent hanging when using
- // a blocking read source like stdin.
- stdin: bool;
+ done: bool; // If an .EOF was reached.
}
#inject Reader {
lines :: lines;
}
-reader_make :: (s: ^Stream, buffer_size := 4096, allocator := context.allocator, greedy := false) -> Reader {
+reader_make :: (s: ^Stream, buffer_size := 4096, allocator := context.allocator) -> Reader {
assert(s.vtable != null, "Stream vtable was not setup correctly.");
reader: Reader;
reader.stream = s;
reader.error = .None;
- reader.greedy = greedy;
memory.alloc_slice(^reader.buffer, buffer_size, allocator);
reader.buffer_allocator = allocator;
write_index := 0;
while n > 0 && !reader_empty(reader) {
- if reader_read_next_chunk(reader) == .ReadPending {
- return write_index, .ReadPending;
+ if err := reader_read_next_chunk(reader);
+ err == .ReadPending || err == .ReadLater
+ {
+ return write_index, err;
}
to_write := math.min(n, end);
lines :: (r: ^Reader, inplace := false) =>
iter.generator(^.{
- r = r, inplace = inplace
-
+ r = r,
+ inplace = inplace
}, (ctx: $C) -> (str, bool) {
line := ctx.r->read_line(consume_newline=true, inplace=ctx.inplace);
return .BufferFull;
}
- if end > 0 && stdin {
+ // If this stream will block on a read, use the data that is present
+ // in the buffer currently. In theory, it would be better to have a
+ // 'stream_poll' that tests if the next read would block, but that
+ // can happen later.
+ if end > 0 && stream.flags & .Block_On_Read {
return .None;
}
// to be read, so this should try again later. This has to return None
// if the end is equal to start because that means that the buffer is
// completely empty.
- if err == .ReadPending {
- if greedy do err = .NoProgress;
+ if err == .ReadPending || err == .ReadLater {
error = err;
return err if end == 0 else .None;
}
handle: Handle;
type: SocketType;
family: SocketDomain;
-
}
// Inject methods for the socket
err := __net_create_socket(^s.handle, domain, type);
if err == .None {
s.vtable = ^__net_socket_vtable;
+ s.flags |= .Block_On_Read;
}
return s, err;
socket_setting :: (s: ^Socket, setting: SocketSetting, value: u32) {
__net_setting(s.handle, setting, value);
+
+ if setting == .NonBlocking {
+ if value > 0 do s.flags = ~~ (cast(u32) s.flags & cast(u32) ~io.Stream_Flags.Block_On_Read);
+ else do s.flags |= io.Stream_Flags.Block_On_Read;
+ }
}
socket_is_alive :: (s: ^Socket) -> bool {
socket_sendto :: (s: ^Socket, data: [] u8, addr: ^Socket_Address) -> i32 {
sent := __net_sendto(s.handle, data, addr);
- // if sent < 0 s.{ s.vtable = null; }
return sent;
}
return sa, received;
}
-host_to_network :: #match {}
+host_to_network :: #match #local {}
#match host_to_network (x: u16) => __net_host_to_net_s(x);
#match host_to_network (x: u32) => __net_host_to_net_l(x);
-network_to_host :: #match {}
+network_to_host :: #match #local {}
#match network_to_host (x: u16) => __net_net_to_host_s(x);
#match network_to_host (x: u32) => __net_net_to_host_l(x);
bytes_read := __net_recv(handle, buffer, ^would_block);
if bytes_read < 0 && !would_block do s.vtable = null;
- if would_block do return .ReadPending, bytes_read;
+ if would_block do return .ReadLater, bytes_read;
return .None, bytes_read;
},