diff --git a/include/lib/xdr.h b/include/lib/xdr.h
new file mode 100644
index 0000000000000000000000000000000000000000..c451a2f6ccecebac200165bf077bcb1df5112978
--- /dev/null
+++ b/include/lib/xdr.h
@@ -0,0 +1,66 @@
+#ifndef XDR_H
+#define XDR_H
+
+#include <stdint.h>
+
+class XDRWriter
+{
+private:
+	XDRWriter(const XDRWriter &copy);
+	char *buffer;
+	uint32_t next_array_len;
+	uint32_t pos;
+	bool is_fixed_length;
+
+public:
+	XDRWriter(char *output_buf) : buffer(output_buf), next_array_len(0), pos(0) {}
+
+	inline void setNextArrayLen(uint32_t len) { next_array_len = len; }
+	inline void setFixedLength() { is_fixed_length = true; }
+	inline void setVariableLength() { is_fixed_length = false; }
+	inline void startList()
+	{
+		if (!is_fixed_length)
+		{
+			put(next_array_len);
+		}
+	}
+
+	inline void put(char c) { put((int32_t)c); }
+	inline void put(unsigned char c) { put((uint32_t)c); }
+	inline void put(uint16_t number) { put((int32_t)number); }
+	inline void put(int16_t number) { put((uint32_t)number); }
+	void put(uint32_t number);
+	void put(int32_t number);
+	void put(uint64_t number);
+	void put(int64_t number);
+	void put(float number);
+	void put(double number);
+	void put(char const *text);
+	template <uint32_t TSize>
+	void put(char const (&text)[TSize]);
+
+	inline uint32_t size() { return pos; }
+};
+
+class XDRReader
+{
+private:
+	XDRReader(const XDRReader &copy);
+	char *data;
+	uint32_t pos;
+
+public:
+	XDRReader(char *d) : pos(0) { data = d; }
+	uint32_t get_uint32();
+	int32_t get_int32();
+	uint64_t get_uint64();
+	int64_t get_int64();
+	float get_float();
+	double get_double();
+	uint32_t get_opaque_length();
+	char *get_opaque(uint32_t length);
+	void get_string(char *target);
+};
+
+#endif
diff --git a/include/lib/xdr16.h b/include/lib/xdr16.h
new file mode 100644
index 0000000000000000000000000000000000000000..483efe44250d65aa6e8e2da6ab1eaba14c9e8e9e
--- /dev/null
+++ b/include/lib/xdr16.h
@@ -0,0 +1,68 @@
+#ifndef XDR16_H
+#define XDR16_H
+
+#include <stdint.h>
+
+class XDRWriter
+{
+private:
+	XDRWriter(const XDRWriter &copy);
+	char *buffer;
+	uint16_t next_array_len;
+	uint16_t pos;
+	bool is_fixed_length;
+
+public:
+	XDRWriter(char *output_buf) : buffer(output_buf), next_array_len(0), pos(0) {}
+
+	inline void setNextArrayLen(uint16_t len) { next_array_len = len; }
+	inline void setFixedLength() { is_fixed_length = true; }
+	inline void setVariableLength() { is_fixed_length = false; }
+	inline void startList()
+	{
+		if (!is_fixed_length)
+		{
+			put(next_array_len);
+		}
+	}
+
+	inline void put(char c) { put((int16_t)c); }
+	inline void put(unsigned char c) { put((uint16_t)c); }
+	void put(uint16_t number);
+	void put(int16_t number);
+	void put(uint32_t number);
+	void put(int32_t number);
+	void put(uint64_t number);
+	void put(int64_t number);
+	void put(float number);
+	void put(double number);
+	void put(char const *text);
+	template <uint16_t TSize>
+	void put(char const (&text)[TSize]);
+
+	inline uint16_t size() { return pos; }
+};
+
+class XDRReader
+{
+private:
+	XDRReader(const XDRReader &copy);
+	char *data;
+	uint32_t pos;
+
+public:
+	XDRReader(char *d) : pos(0) { data = d; }
+	int16_t get_int16();
+	uint16_t get_uint16();
+	uint32_t get_uint32();
+	int32_t get_int32();
+	uint64_t get_uint64();
+	int64_t get_int64();
+	float get_float();
+	double get_double();
+	uint32_t get_opaque_length();
+	char *get_opaque(uint32_t length);
+	void get_string(char *target);
+};
+
+#endif
diff --git a/src/app/prototest/Makefile.inc b/src/app/prototest/Makefile.inc
index ee509a731a94aa51e84a1f8316dce2cd5b3790ae..c088f6bb73bafe1d1a6cb238785c6aacdec67cb2 100644
--- a/src/app/prototest/Makefile.inc
+++ b/src/app/prototest/Makefile.inc
@@ -69,12 +69,12 @@ endif
 
 ifeq (${prototest_xdr}, 1)
 	COMMON_FLAGS += -DPROTOTEST_XDR
-	CXX_TARGETS += src/os/object/xdrstream.cc src/os/object/xdrinput.cc
+	CXX_TARGETS += src/lib/xdr.cc
 endif
 
 ifeq (${prototest_xdr16}, 1)
 	COMMON_FLAGS += -DPROTOTEST_XDR16
-	CXX_TARGETS += src/os/object/xdr16stream.cc src/os/object/xdr16input.cc
+	CXX_TARGETS += src/lib/xdr16.cc
 endif
 
 # Don't try to make .capnp from .capnp.c
diff --git a/src/app/prototest/main.cc b/src/app/prototest/main.cc
index ee5c958b089911d46fdbbe0fbe91c49e206184b0..7fc897edec8439003515c4f536773819b10000d5 100644
--- a/src/app/prototest/main.cc
+++ b/src/app/prototest/main.cc
@@ -35,14 +35,10 @@
 #include "ubj.h"
 #endif
 #ifdef PROTOTEST_XDR
-#include "object/stdbuf.h"
-#include "object/xdrstream.h"
-#include "object/xdrinput.h"
+#include "lib/xdr.h"
 #endif
 #ifdef PROTOTEST_XDR16
-#include "object/stdbuf.h"
-#include "object/xdr16stream.h"
-#include "object/xdr16input.h"
+#include "lib/xdr16.h"
 #endif
 
 #include <stdint.h>
diff --git a/src/lib/xdr.cc b/src/lib/xdr.cc
new file mode 100644
index 0000000000000000000000000000000000000000..180bff096a3785d30e3723d5f1336e8d6daa30e2
--- /dev/null
+++ b/src/lib/xdr.cc
@@ -0,0 +1,187 @@
+#include "lib/xdr.h"
+
+
+void XDRWriter::put(uint32_t number)
+{
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 4;
+}
+
+void XDRWriter::put(int32_t number)
+{
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 4;
+}
+
+void XDRWriter::put(uint64_t number)
+{
+	*buffer++ = ((number >> 56) & 0xffU);
+	*buffer++ = ((number >> 48) & 0xffU);
+	*buffer++ = ((number >> 40) & 0xffU);
+	*buffer++ = ((number >> 32) & 0xffU);
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 8;
+}
+
+void XDRWriter::put(int64_t number)
+{
+	*buffer++ = ((number >> 56) & 0xffU);
+	*buffer++ = ((number >> 48) & 0xffU);
+	*buffer++ = ((number >> 40) & 0xffU);
+	*buffer++ = ((number >> 32) & 0xffU);
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 8;
+}
+
+void XDRWriter::put(float number)
+{
+	union {
+		uint32_t i;
+		float f;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.f = number;
+	put(v.i);
+}
+
+void XDRWriter::put(double number)
+{
+	union {
+		uint64_t i;
+		double d;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.d = number;
+	put(v.i);
+}
+
+void XDRWriter::put(char const *data){
+	if (!is_fixed_length) {
+		put(next_array_len);
+	}
+	uint32_t i;
+	for (i = 0; i < next_array_len; i++) {
+		*buffer++ = data[i];
+	}
+	while ((i++) % 4 != 0){
+		*buffer++ = 0;
+	}
+	pos += i;
+}
+
+template<uint32_t TSize>
+void XDRWriter::put(char const (&data)[TSize]){
+	if (!is_fixed_length) {
+		put(TSize);
+	}
+	uint32_t i;
+	for (i = 0; i < TSize; i++) {
+		*buffer++ = data[i];
+	}
+	while ((i++) % 4 != 0){
+		*buffer++ = 0;
+	}
+	pos += i;
+}
+
+uint32_t XDRReader::get_uint32()
+{
+	uint32_t ret = ((uint8_t)data[pos]<<24) | ((uint8_t)data[pos+1]<<16)
+		| ((uint8_t)data[pos+2]<<8) | (uint8_t)data[pos+3];
+	pos += 4;
+	return ret;
+}
+
+int32_t XDRReader::get_int32()
+{
+	int32_t ret = (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | data[pos+3];
+	pos += 4;
+	return ret;
+}
+
+uint64_t XDRReader::get_uint64()
+{
+	uint64_t ret0 = ((uint8_t)data[pos]<<24) | ((uint8_t)data[pos+1]<<16)
+		| ((uint8_t)data[pos+2]<<8) | (uint8_t)data[pos+3];
+	pos += 4;
+	uint64_t ret1 = ((uint8_t)data[pos]<<24) | ((uint8_t)data[pos+1]<<16)
+		| ((uint8_t)data[pos+2]<<8) | (uint8_t)data[pos+3];
+	pos += 4;
+	return (ret0 << 32) | ret1;
+}
+
+int64_t XDRReader::get_int64()
+{
+	int64_t ret0 = (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | data[pos+3];
+	pos += 4;
+	int64_t ret1 = (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | data[pos+3];
+	pos += 4;
+	return (ret0 << 32) | ret1;
+}
+
+float XDRReader::get_float()
+{
+	union {
+		uint32_t i;
+		float f;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.i = get_uint32();
+	return v.f;
+}
+
+double XDRReader::get_double()
+{
+	union {
+		uint64_t i;
+		double d;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.i = get_uint64();
+	return v.d;
+}
+
+uint32_t XDRReader::get_opaque_length()
+{
+	return get_uint32();
+}
+
+char *XDRReader::get_opaque(uint32_t length)
+{
+	char *ret = data + pos;
+	pos += length;
+	if (length % 4) {
+		pos += 4 - (length % 4);
+	}
+	return ret;
+}
+
+void XDRReader::get_string(char* target)
+{
+	uint16_t length = get_opaque_length();
+	uint16_t i;
+	for (i = 0; i < length; i++) {
+		target[i] = data[pos + i];
+	}
+	target[i] = 0;
+	pos += length;
+	if (length % 4) {
+		pos += 4 - (length % 4);
+	}
+}
diff --git a/src/lib/xdr16.cc b/src/lib/xdr16.cc
new file mode 100644
index 0000000000000000000000000000000000000000..2f684636159457067629b556e4cc8ab267b0c82f
--- /dev/null
+++ b/src/lib/xdr16.cc
@@ -0,0 +1,214 @@
+#include "lib/xdr16.h"
+
+void XDRWriter::put(uint16_t number)
+{
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 2;
+}
+
+void XDRWriter::put(int16_t number)
+{
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 2;
+}
+
+void XDRWriter::put(uint32_t number)
+{
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 4;
+}
+
+void XDRWriter::put(int32_t number)
+{
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 4;
+}
+
+void XDRWriter::put(uint64_t number)
+{
+	*buffer++ = ((number >> 56) & 0xffU);
+	*buffer++ = ((number >> 48) & 0xffU);
+	*buffer++ = ((number >> 40) & 0xffU);
+	*buffer++ = ((number >> 32) & 0xffU);
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 8;
+}
+
+void XDRWriter::put(int64_t number)
+{
+	*buffer++ = ((number >> 56) & 0xffU);
+	*buffer++ = ((number >> 48) & 0xffU);
+	*buffer++ = ((number >> 40) & 0xffU);
+	*buffer++ = ((number >> 32) & 0xffU);
+	*buffer++ = ((number >> 24) & 0xffU);
+	*buffer++ = ((number >> 16) & 0xffU);
+	*buffer++ = ((number >> 8) & 0xffU);
+	*buffer++ = (number & 0xffU);
+	pos += 8;
+}
+
+void XDRWriter::put(float number)
+{
+	union {
+		uint32_t i;
+		float f;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.f = number;
+	put(v.i);
+}
+
+void XDRWriter::put(double number)
+{
+	union {
+		uint64_t i;
+		double d;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.d = number;
+	put(v.i);
+}
+
+void XDRWriter::put(char const *data){
+	if (!is_fixed_length) {
+		put(next_array_len);
+	}
+	uint16_t i;
+	for (i = 0; i < next_array_len; i++) {
+		*buffer++ = data[i];
+	}
+	while ((i++) % 2 != 0){
+		*buffer++ = 0;
+	}
+	pos += i;
+}
+
+template<uint16_t TSize>
+void XDRWriter::put(char const (&data)[TSize]){
+	if (!is_fixed_length) {
+		put(TSize);
+	}
+	uint16_t i;
+	for (i = 0; i < TSize; i++) {
+		*buffer++ = data[i];
+	}
+	while ((i++) % 2 != 0){
+		*buffer++ = 0;
+	}
+	pos += i;
+}
+
+uint16_t XDRReader::get_uint16()
+{
+	uint16_t ret = ((uint8_t)data[pos]<<8) | (uint8_t)data[pos+1];
+	pos += 2;
+	return ret;
+}
+
+int16_t XDRReader::get_int16()
+{
+	int16_t ret = (data[pos]<<8) | data[pos+1];
+	pos += 2;
+	return ret;
+}
+
+uint32_t XDRReader::get_uint32()
+{
+	uint32_t ret = ((uint8_t)data[pos]<<24) | ((uint8_t)data[pos+1]<<16)
+		| ((uint8_t)data[pos+2]<<8) | (uint8_t)data[pos+3];
+	pos += 4;
+	return ret;
+}
+
+int32_t XDRReader::get_int32()
+{
+	int32_t ret = (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | data[pos+3];
+	pos += 4;
+	return ret;
+}
+
+uint64_t XDRReader::get_uint64()
+{
+	uint64_t ret0 = ((uint8_t)data[pos]<<24) | ((uint8_t)data[pos+1]<<16)
+		| ((uint8_t)data[pos+2]<<8) | (uint8_t)data[pos+3];
+	pos += 4;
+	uint64_t ret1 = ((uint8_t)data[pos]<<24) | ((uint8_t)data[pos+1]<<16)
+		| ((uint8_t)data[pos+2]<<8) | (uint8_t)data[pos+3];
+	pos += 4;
+	return (ret0 << 32) | ret1;
+}
+
+int64_t XDRReader::get_int64()
+{
+	int64_t ret0 = (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | data[pos+3];
+	pos += 4;
+	int64_t ret1 = (data[pos]<<24) | (data[pos+1]<<16) | (data[pos+2]<<8) | data[pos+3];
+	pos += 4;
+	return (ret0 << 32) | ret1;
+}
+
+float XDRReader::get_float()
+{
+	union {
+		uint32_t i;
+		float f;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.i = get_uint32();
+	return v.f;
+}
+
+double XDRReader::get_double()
+{
+	union {
+		uint64_t i;
+		double d;
+	} v;
+	// Setting one member of a struct and then reading another is undefined
+	// behaviour, but works as intended in nearly any (embedded) compiler
+	v.i = get_uint64();
+	return v.d;
+}
+
+uint32_t XDRReader::get_opaque_length()
+{
+	return get_uint16();
+}
+
+char *XDRReader::get_opaque(uint32_t length)
+{
+	char *ret = data + pos;
+	pos += length;
+	if (length % 2) {
+		pos += 2 - (length % 2);
+	}
+	return ret;
+}
+
+void XDRReader::get_string(char* target)
+{
+	uint16_t length = get_opaque_length();
+	uint16_t i;
+	for (i = 0; i < length; i++) {
+		target[i] = data[pos + i];
+	}
+	target[i] = 0;
+	pos += length;
+	if (length % 2) {
+		pos += 2 - (length % 2);
+	}
+}