|
@@ -0,0 +1,108 @@
|
|
|
+
|
|
|
+const std = @import("std");
|
|
|
+const mem = std.mem;
|
|
|
+const fmt = std.fmt;
|
|
|
+const debug = std.debug;
|
|
|
+
|
|
|
+
|
|
|
+// read n bytes or return err
|
|
|
+fn readExactly(reader: anytype, buffer: []u8) !void{
|
|
|
+ var have: usize = 0;
|
|
|
+ while(have < buffer.len){
|
|
|
+ const amt = try reader.read(buffer[have..]);
|
|
|
+ if(amt == 0){ return error.EndOfStream; }
|
|
|
+ have += amt;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+pub fn main() !void{
|
|
|
+ const stdin = std.io.getStdIn().reader();
|
|
|
+ const stdout = std.io.getStdOut().writer();
|
|
|
+
|
|
|
+ var buffer: [64]u8 = [_]u8{0} ** 64;
|
|
|
+ var len_bytes_buf: [2]u8 = [_]u8{0} ** 2;
|
|
|
+ var reply_buffer: [64]u8 = [_]u8{0} ** 64;
|
|
|
+
|
|
|
+ while(true){
|
|
|
+ // read msg length (2 bytes, big-endian)
|
|
|
+ readExactly(stdin, &len_bytes_buf) catch |err|{
|
|
|
+ if(err == error.EndOfStream){ break; }
|
|
|
+ return err;
|
|
|
+ };
|
|
|
+
|
|
|
+ const msg_len = @as(u16, len_bytes_buf[0]) << 8 | len_bytes_buf[1]; // convert to u16, big-endian
|
|
|
+
|
|
|
+ //std.debug.print("Full buffer: ", .{});
|
|
|
+ //for(buffer[0..msg_len]) |b|{
|
|
|
+ // std.debug.print("{c}", .{b});
|
|
|
+ //}
|
|
|
+ //std.debug.print("\n", .{});
|
|
|
+
|
|
|
+ if(msg_len == 0 or msg_len > buffer.len){
|
|
|
+ try stdout.writeAll(&[2]u8{ 0, 0 });
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ readExactly(stdin, buffer[0..msg_len]) catch |err|{ // read msg
|
|
|
+ if(err == error.EndOfStream){ break; }
|
|
|
+ return err;
|
|
|
+ };
|
|
|
+ //debug.print("RECV: '{s}'\n", .{buffer[0..msg_len]});
|
|
|
+
|
|
|
+ var fbs = std.io.fixedBufferStream(&reply_buffer);
|
|
|
+ var writer = fbs.writer();
|
|
|
+
|
|
|
+
|
|
|
+ if(mem.eql(u8, buffer[0..msg_len], "hello")){
|
|
|
+ try writer.writeAll("Hello world!");
|
|
|
+
|
|
|
+ }else if(mem.startsWith(u8, buffer[0..msg_len], "factorial ")){
|
|
|
+ const prefix_len = "factorial ".len;
|
|
|
+ const n_str = buffer[prefix_len..msg_len];
|
|
|
+ //std.debug.print("n_str = '{s}' ({} chars)\n", .{ n_str, n_str.len });
|
|
|
+
|
|
|
+ const n = fmt.parseInt(isize, n_str, 10) catch {
|
|
|
+ //std.debug.print("error:PARSE ERROR for '{s}'\n", .{n_str});
|
|
|
+ try writer.writeAll("error:badarg");
|
|
|
+ //try writer.writeAll("0");
|
|
|
+ try sendResponse(stdout, fbs.getWritten());
|
|
|
+ continue;
|
|
|
+ };
|
|
|
+
|
|
|
+ if(n < 0){
|
|
|
+ //try writer.writeAll("error:badarg");
|
|
|
+ //std.debug.print("n < 0 -> badarg\n", .{});
|
|
|
+ //try writer.writeAll("0");
|
|
|
+ try writer.writeAll("error:badarg: n < 0");
|
|
|
+ }else if(n > 34){
|
|
|
+ //std.debug.print("n > 34 -> too large\n", .{});
|
|
|
+ //try writer.writeAll("0");
|
|
|
+ try writer.writeAll("error:input too large, max is 34");
|
|
|
+ }else{
|
|
|
+ var result: u128 = 1;
|
|
|
+ var i = @as(u128, @intCast(n));
|
|
|
+ while (i > 1) : (i -= 1) result *= i;
|
|
|
+ //std.debug.print("factorial({d}) = {d}\n", .{n, result});
|
|
|
+ try fmt.formatInt(result, 10, .lower, .{}, writer);
|
|
|
+ }
|
|
|
+
|
|
|
+ }else{
|
|
|
+ try writer.writeAll("error:unknown command");
|
|
|
+ }
|
|
|
+
|
|
|
+ try sendResponse(stdout, fbs.getWritten());
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+fn sendResponse(writer: anytype, response: []const u8) !void{
|
|
|
+ const len: u16 = @intCast(response.len);
|
|
|
+
|
|
|
+ //std.debug.print("SEND: len={d} (0x{X:0>4}) data='{s}'\n", .{ len, len, response });
|
|
|
+
|
|
|
+ try writer.writeByte(@intCast((len >> 8) & 0xFF)); // high byte
|
|
|
+ try writer.writeByte(@intCast(len & 0xFF)); // low byte
|
|
|
+ try writer.writeAll(response);
|
|
|
+}
|
|
|
+
|