aboutsummaryrefslogtreecommitdiff
path: root/lib/libk
diff options
context:
space:
mode:
Diffstat (limited to 'lib/libk')
-rw-r--r--lib/libk/BUILD.bazel80
-rw-r--r--lib/libk/endian/little.c4
-rw-r--r--lib/libk/endian/test_endian_little.cc38
-rw-r--r--lib/libk/include/endian.h (renamed from lib/libk/endian.h)8
-rw-r--r--lib/libk/include/stdio.h (renamed from lib/libk/stdio.h)41
-rw-r--r--lib/libk/include/stdlib.h (renamed from lib/libk/stdlib.h)10
-rw-r--r--lib/libk/include/string.h14
-rw-r--r--lib/libk/stdio/fprintf.c5
-rw-r--r--lib/libk/stdio/printf.c5
-rw-r--r--lib/libk/stdio/vfprintf.c (renamed from lib/libk/stdio/vfprintf.cpp)25
-rw-r--r--lib/libk/stdlib/linked_list_allocator.c15
-rw-r--r--lib/libk/stdlib/memcpy.c2
-rw-r--r--lib/libk/stdlib/memset.c3
-rw-r--r--lib/libk/stdlib/test_allocator.hh22
-rw-r--r--lib/libk/stdlib/test_linked_list_allocator.cc98
-rw-r--r--lib/libk/stdlib/test_mem.cc29
-rw-r--r--lib/libk/string.h28
-rw-r--r--lib/libk/string/itoa.c6
-rw-r--r--lib/libk/string/test_string.cc20
19 files changed, 352 insertions, 101 deletions
diff --git a/lib/libk/BUILD.bazel b/lib/libk/BUILD.bazel
new file mode 100644
index 0000000..51b4c1b
--- /dev/null
+++ b/lib/libk/BUILD.bazel
@@ -0,0 +1,80 @@
+filegroup(
+ name = "k_srcs",
+ srcs = [
+ "endian/little.c",
+ "stdio/fprintf.c",
+ "stdio/printf.c",
+ "stdio/vfprintf.c",
+ "stdlib/linked_list_allocator.c",
+ "stdlib/memcpy.c",
+ "stdlib/memset.c",
+ "string/itoa.c",
+ ],
+)
+
+cc_library(
+ name = "k",
+ srcs = [":k_srcs"],
+ hdrs = glob(["include/*.h"]),
+ includes = ["include"],
+ target_compatible_with = [
+ "@platforms//os:none",
+ ],
+ visibility = ["//visibility:public"],
+)
+
+# tests
+cc_library(
+ name = "k_sut",
+ includes = ["."],
+ target_compatible_with = select({
+ "@platforms//os:none": ["@platforms//:incompatible"],
+ "//conditions:default": [],
+ }),
+ textual_hdrs = [":k_srcs"],
+)
+
+cc_test(
+ name = "test_endian_little",
+ srcs = [
+ "endian/test_endian_little.cc",
+ ],
+ deps = [
+ ":k_sut",
+ "@googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "test_linked_list_allocator",
+ srcs = [
+ "stdlib/test_allocator.hh",
+ "stdlib/test_linked_list_allocator.cc",
+ ],
+ deps = [
+ ":k_sut",
+ "@googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "test_mem",
+ srcs = [
+ "stdlib/test_mem.cc",
+ ],
+ deps = [
+ ":k_sut",
+ "@googletest//:gtest_main",
+ ],
+)
+
+cc_test(
+ name = "test_string",
+ srcs = [
+ "string/test_string.cc",
+ ],
+ deps = [
+ ":k_sut",
+ "@googletest//:gtest_main",
+ ],
+)
diff --git a/lib/libk/endian/little.c b/lib/libk/endian/little.c
index 042bb55..56a20ad 100644
--- a/lib/libk/endian/little.c
+++ b/lib/libk/endian/little.c
@@ -1,6 +1,4 @@
-//=====================================================================
-// spdx-license-identifier: ISC
-//=====================================================================
+/* spdx-license-identifier: ISC */
#include "endian.h"
diff --git a/lib/libk/endian/test_endian_little.cc b/lib/libk/endian/test_endian_little.cc
new file mode 100644
index 0000000..97ee286
--- /dev/null
+++ b/lib/libk/endian/test_endian_little.cc
@@ -0,0 +1,38 @@
+#include <endian.h>
+#include <gtest/gtest.h>
+
+namespace libk {
+#include "little.c"
+} // namespace libk
+
+TEST(endian_little, htole16)
+{
+ EXPECT_EQ(static_cast<uint16_t>(0xabcd), libk::htole16(0xabcd));
+ EXPECT_EQ(libk::htole16(0xabcd), htole16(0xabcd));
+}
+TEST(endian_little, htole32)
+{
+ EXPECT_EQ(static_cast<uint32_t>(0xabcd0123), libk::htole32(0xabcd0123));
+ EXPECT_EQ(libk::htole32(0xabcd0123), htole32(0xabcd0123));
+}
+TEST(endian_little, htole64)
+{
+ EXPECT_EQ(static_cast<uint64_t>(0x0123456789abcdef), libk::htole64(0x0123456789abcdef));
+ EXPECT_EQ(libk::htole64(0xabcdef0123456789), htole64(0xabcdef0123456789));
+}
+
+TEST(endian_little, htobe16)
+{
+ EXPECT_EQ(static_cast<uint16_t>(0xabcd), libk::htobe16(0xcdab));
+ EXPECT_EQ(libk::htobe16(0xabcd), htobe16(0xabcd));
+}
+TEST(endian_little, htobe32)
+{
+ EXPECT_EQ(static_cast<uint32_t>(0xabcd0123), libk::htobe32(0x2301cdab));
+ EXPECT_EQ(libk::htobe32(0xabcd0123), htobe32(0xabcd0123));
+}
+TEST(endian_little, htobe64)
+{
+ EXPECT_EQ(static_cast<uint64_t>(0x0123456789abcdef), libk::htobe64(0xefcdab8967452301));
+ EXPECT_EQ(libk::htobe64(0xabcdef0123456789), htobe64(0xabcdef0123456789));
+}
diff --git a/lib/libk/endian.h b/lib/libk/include/endian.h
index 70bc5f7..6aa2669 100644
--- a/lib/libk/endian.h
+++ b/lib/libk/include/endian.h
@@ -1,13 +1,11 @@
-//=====================================================================
-// spdx-license-identifier: ISC
-//=====================================================================
+/* spdx-license-identifier: ISC */
#pragma once
#include <stdint.h>
-// These functions convert the byte encoding of integer values from host byte order to and from little-endian and
-// big-endian byte order
+/* These functions convert the byte encoding of integer values from host byte order to and from little-endian and
+ * big-endian byte order */
uint16_t htole16(uint16_t host_16b);
uint32_t htole32(uint32_t host_32b);
uint64_t htole64(uint64_t host_64b);
diff --git a/lib/libk/stdio.h b/lib/libk/include/stdio.h
index 5ef68f1..7a6e663 100644
--- a/lib/libk/stdio.h
+++ b/lib/libk/include/stdio.h
@@ -2,27 +2,19 @@
#include <stdarg.h>
-///@defgroup libk libk
-///@{
-///@defgroup stdio stdio
-///@{
+/** An object type used for streams */
+typedef struct FILE {
+ int id;
-#ifdef __cplusplus
-/**
- * An object type used for streams
- */
-struct kIoDevice {
/** Function that prints a character to the stream */
- virtual void putc(char) = 0;
+ void (*putc)(const struct FILE *, char);
+
/** Function that prints a string to the stream */
- virtual int puts(const char *, int) = 0;
+ int (*puts)(const struct FILE *, const char *, int);
+
/** Flush write buffers */
- virtual void flush() = 0;
-};
-typedef kIoDevice FILE;
-#else
-typedef void FILE;
-#endif
+ void (*flush)(const struct FILE *);
+} FILE;
/** A FILE value corresponding to stdin, the keyboard buffer */
extern FILE *stdin;
@@ -31,28 +23,19 @@ extern FILE *stdout;
/** A FILE value corresponding to stderr, the uart */
extern FILE *stderr;
-#ifdef __cplusplus
-extern "C" {
-#endif
/**
* Write the formatted string to stdout
* Supports ``%s`` (string), ``%d`` (decimal), ``%u`` (unsigned), ``%x`` (hexadecimal)
* @return number of bytes written
*/
-int printf(const char *restrict format, ...);
+int printf(const char *__restrict__ format, ...);
/**
* Write the formatted string to stream; see printf
*/
-int fprintf(FILE *restrict stream, const char *restrict format, ...);
+int fprintf(FILE *__restrict__ stream, const char *__restrict__ format, ...);
/**
* Write the formatted string to stream; see printf
*/
-int vfprintf(FILE *restrict stream, const char *restrict format, va_list ap);
-#ifdef __cplusplus
-}
-#endif
-
-///@}
-///@}
+int vfprintf(FILE *__restrict__ stream, const char *__restrict__ format, va_list ap);
diff --git a/lib/libk/stdlib.h b/lib/libk/include/stdlib.h
index 84d9b2d..143c931 100644
--- a/lib/libk/stdlib.h
+++ b/lib/libk/include/stdlib.h
@@ -2,11 +2,6 @@
#include <stddef.h>
-///@defgroup libk libk
-///@{
-///@defgroup stdlib stdlib
-///@{
-
/**
* Allocate size bytes and return a pointer to the allocated memory
*/
@@ -25,7 +20,4 @@ void *memset(void *s, int c, long unsigned n);
/**
* Copy n bytes from memory area src to memory area dest. The memory areas must not overlap.
*/
-void *memcpy(void *restrict dest, const void *restrict src, long unsigned n);
-
-///@}
-///@}
+void *memcpy(void *__restrict__ dest, const void *__restrict__ src, long unsigned n);
diff --git a/lib/libk/include/string.h b/lib/libk/include/string.h
new file mode 100644
index 0000000..45b05a5
--- /dev/null
+++ b/lib/libk/include/string.h
@@ -0,0 +1,14 @@
+#pragma once
+
+#define OCTAL 8
+#define DECIMAL 10
+#define HEX 16
+
+/**
+ * Convert int into a string
+ */
+char *itoa(char *p, int x, unsigned base);
+/**
+ * Convert unsigned int into a string
+ */
+char *utoa(char *p, unsigned x, unsigned base);
diff --git a/lib/libk/stdio/fprintf.c b/lib/libk/stdio/fprintf.c
index 9a96dc6..c088f54 100644
--- a/lib/libk/stdio/fprintf.c
+++ b/lib/libk/stdio/fprintf.c
@@ -1,11 +1,12 @@
#include <stdio.h>
int
-fprintf(FILE *restrict stream, const char *restrict format, ...)
+fprintf(FILE *__restrict__ stream, const char *__restrict__ format, ...)
{
+ int c = 0;
va_list ap;
va_start(ap, format);
- int c = vfprintf(stream, format, ap);
+ c += vfprintf(stream, format, ap);
va_end(ap);
return c;
}
diff --git a/lib/libk/stdio/printf.c b/lib/libk/stdio/printf.c
index 4efc1ac..4c45593 100644
--- a/lib/libk/stdio/printf.c
+++ b/lib/libk/stdio/printf.c
@@ -1,11 +1,12 @@
#include <stdio.h>
int
-printf(const char *restrict format, ...)
+printf(const char *__restrict__ format, ...)
{
+ int c = 0;
va_list ap;
va_start(ap, format);
- int c = vfprintf(stdout, format, ap);
+ c += vfprintf(stdout, format, ap);
va_end(ap);
return c;
}
diff --git a/lib/libk/stdio/vfprintf.cpp b/lib/libk/stdio/vfprintf.c
index aa9256d..807c26a 100644
--- a/lib/libk/stdio/vfprintf.cpp
+++ b/lib/libk/stdio/vfprintf.c
@@ -3,40 +3,41 @@
static char buffer[3 * sizeof(int) + 2];
-extern "C" int
-vfprintf(FILE *restrict stream, const char *restrict format, va_list params)
+int
+vfprintf(FILE *__restrict__ stream, const char *__restrict__ format, va_list params)
{
int written = 0;
+ int i;
int s = 0;
int l = 0;
- for (int i = 0; format[i] != '\0'; ++i) {
+ for (i = 0; format[i] != '\0'; ++i) {
if (format[i] == '%') {
- written += stream->puts(&format[s], l);
+ written += stream->puts(stream, &format[s], l);
s = i + 2;
++i;
switch (format[i]) {
case 's': {
const char *arg = va_arg(params, const char *);
- written += stream->puts(arg, -1);
+ written += stream->puts(stream, arg, -1);
} break;
case 'c': {
- const int arg = va_arg(params, int);
- stream->putc(arg);
+ const char arg = (char)va_arg(params, int);
+ stream->putc(stream, arg);
++written;
} break;
case 'd': {
const char *arg = itoa(buffer, va_arg(params, int), 10);
- written += stream->puts(arg, -1);
+ written += stream->puts(stream, arg, -1);
} break;
case 'u': {
const char *arg = utoa(buffer, va_arg(params, unsigned int), 10);
- written += stream->puts(arg, -1);
+ written += stream->puts(stream, arg, -1);
} break;
case 'x': {
const char *arg = utoa(buffer, va_arg(params, unsigned int), 16);
- written += stream->puts(arg, -1);
+ written += stream->puts(stream, arg, -1);
} break;
}
@@ -47,8 +48,8 @@ vfprintf(FILE *restrict stream, const char *restrict format, va_list params)
++l;
}
- if (l > 0) { written += stream->puts(&format[s], l); }
+ if (l > 0) { written += stream->puts(stream, &format[s], l); }
- stream->flush();
+ stream->flush(stream);
return written;
}
diff --git a/lib/libk/stdlib/linked_list_allocator.c b/lib/libk/stdlib/linked_list_allocator.c
index 66c63d1..bcec580 100644
--- a/lib/libk/stdlib/linked_list_allocator.c
+++ b/lib/libk/stdlib/linked_list_allocator.c
@@ -31,13 +31,14 @@ alloc_init(void *mem, size_t size)
void *
malloc(size_t size)
{
+ struct Chunk *iter;
if (begin == NULL) return NULL;
- // find free chunk that is at least (size + sizeof(struct Chunk))
- for (struct Chunk *iter = begin; iter != NULL; iter = iter->next) {
+ /* find free chunk that is at least (size + sizeof(struct Chunk)) */
+ for (iter = begin; iter != NULL; iter = iter->next) {
if (iter->used != 0 || iter->size < size) continue;
- // if there's at least sizeof(struct Chunk) bytes left over, create a new Chunk
+ /* if there's at least sizeof(struct Chunk) bytes left over, create a new Chunk */
if (iter->size >= (size + 2 * sizeof(struct Chunk))) {
struct Chunk *next = (struct Chunk *)((uintptr_t)iter + sizeof(struct Chunk) + size);
Chunk_ctor(next, iter->size - size - sizeof(struct Chunk));
@@ -57,18 +58,20 @@ malloc(size_t size)
void
free(void *ptr)
{
+ struct Chunk *chunk;
if (ptr == NULL) return;
- struct Chunk *chunk = (struct Chunk *)((uintptr_t)ptr - sizeof(struct Chunk));
+
+ chunk = (struct Chunk *)((uintptr_t)ptr - sizeof(struct Chunk));
chunk->used = 0;
- // merge next chunk
+ /* merge next chunk */
if (chunk->next != NULL && chunk->next->used == 0) {
chunk->size += chunk->next->size + sizeof(struct Chunk);
chunk->next = chunk->next->next;
if (chunk->next != NULL) chunk->next->prev = chunk;
}
- // merge into prev chunk
+ /* merge into prev chunk */
if (chunk->prev != NULL && chunk->prev->used == 0) {
chunk->prev->size += chunk->size + sizeof(struct Chunk);
chunk->prev->next = chunk->next;
diff --git a/lib/libk/stdlib/memcpy.c b/lib/libk/stdlib/memcpy.c
index 90470d5..db7d21e 100644
--- a/lib/libk/stdlib/memcpy.c
+++ b/lib/libk/stdlib/memcpy.c
@@ -1,5 +1,5 @@
void *
-memcpy(void *restrict dest, const void *restrict src, long unsigned n)
+memcpy(void *__restrict__ dest, const void *__restrict__ src, long unsigned n)
{
char *pDest = (char *)dest;
const char *pSrc = (const char *)src;
diff --git a/lib/libk/stdlib/memset.c b/lib/libk/stdlib/memset.c
index a16bd05..2a86f8e 100644
--- a/lib/libk/stdlib/memset.c
+++ b/lib/libk/stdlib/memset.c
@@ -1,7 +1,8 @@
void *
memset(void *s, int c, long unsigned n)
{
+ unsigned i;
char *pDest = (char *)s;
- for (unsigned i = 0; i < n; ++i) pDest[i] = (char)c;
+ for (i = 0; i < n; ++i) pDest[i] = (char)c;
return s;
}
diff --git a/lib/libk/stdlib/test_allocator.hh b/lib/libk/stdlib/test_allocator.hh
new file mode 100644
index 0000000..3bc1715
--- /dev/null
+++ b/lib/libk/stdlib/test_allocator.hh
@@ -0,0 +1,22 @@
+#pragma once
+
+class TestAllocator : public ::testing::Test {
+protected:
+ void
+ SetUp() override
+ {
+ memory = malloc(memory_size);
+ libk::alloc_init(memory, memory_size);
+ ASSERT_EQ(libk::begin, memory);
+ }
+
+ void
+ TearDown() override
+ {
+ free(memory);
+ libk::begin = nullptr;
+ }
+
+ const size_t memory_size = 4096;
+ void *memory = nullptr;
+};
diff --git a/lib/libk/stdlib/test_linked_list_allocator.cc b/lib/libk/stdlib/test_linked_list_allocator.cc
new file mode 100644
index 0000000..5963ce1
--- /dev/null
+++ b/lib/libk/stdlib/test_linked_list_allocator.cc
@@ -0,0 +1,98 @@
+#include <gtest/gtest.h>
+#include <iomanip>
+#include <iostream>
+
+namespace libk {
+#include "linked_list_allocator.c"
+
+std::ostream &
+operator<<(std::ostream &os, const Chunk &b)
+{
+ for (const Chunk *iter = &b; iter != nullptr; iter = iter->next) {
+ os << iter << " used=" << iter->used << " size=" << std::setw(4) << iter->size << " next=" << iter->next
+ << std::endl;
+ }
+ return os;
+}
+}; // namespace libk
+
+#include "test_allocator.hh"
+
+TEST(UninitializedAllocator, malloc) { EXPECT_EQ(libk::malloc(1024), nullptr); }
+
+TEST_F(TestAllocator, mallocMoreThanAvialable)
+{
+ void *ptr = libk::malloc(memory_size);
+ EXPECT_EQ(ptr, nullptr) << *libk::begin;
+}
+
+TEST_F(TestAllocator, mallocExactlyAvialable)
+{
+ void *ptr = libk::malloc(memory_size - sizeof(libk::Chunk));
+ EXPECT_NE(ptr, nullptr) << *libk::begin;
+}
+
+TEST_F(TestAllocator, malloc)
+{
+ libk::Chunk *begin = libk::begin;
+
+ void *ptr0 = libk::malloc(1024);
+ EXPECT_NE(ptr0, nullptr) << *libk::begin;
+ EXPECT_EQ(reinterpret_cast<std::uintptr_t>(ptr0),
+ reinterpret_cast<std::uintptr_t>(libk::begin) + sizeof(libk::Chunk));
+ EXPECT_EQ(begin->used, 1);
+ EXPECT_EQ(begin->size, 1024);
+ ASSERT_NE(begin->next, nullptr);
+ begin = begin->next;
+
+ void *ptr1 = libk::malloc(512);
+ EXPECT_NE(ptr1, nullptr) << *libk::begin;
+ EXPECT_EQ(reinterpret_cast<std::uintptr_t>(ptr1),
+ reinterpret_cast<std::uintptr_t>(libk::begin) + 2 * sizeof(libk::Chunk) + 1024);
+ EXPECT_EQ(begin->used, 1);
+ EXPECT_EQ(begin->size, 512);
+ ASSERT_NE(begin->next, nullptr);
+}
+
+TEST_F(TestAllocator, freeNullptr)
+{
+ void *ptr0 = libk::malloc(1024);
+ EXPECT_NE(ptr0, nullptr) << *libk::begin;
+ void *ptr1 = libk::malloc(512);
+ EXPECT_NE(ptr1, nullptr) << *libk::begin;
+
+ libk::free(nullptr);
+ libk::Chunk *begin = libk::begin;
+ EXPECT_EQ(begin->used, 1);
+ EXPECT_NE(begin->next, nullptr);
+ begin = begin->next;
+ EXPECT_EQ(begin->used, 1);
+ EXPECT_NE(begin->next, nullptr);
+ begin = begin->next;
+ EXPECT_EQ(begin->used, 0);
+ EXPECT_EQ(begin->next, nullptr);
+}
+
+TEST_F(TestAllocator, free)
+{
+ void *ptr0 = libk::malloc(1024);
+ EXPECT_NE(ptr0, nullptr) << *libk::begin;
+ void *ptr1 = libk::malloc(512);
+ EXPECT_NE(ptr1, nullptr) << *libk::begin;
+
+ libk::free(ptr0);
+ libk::Chunk *begin = libk::begin;
+ EXPECT_EQ(begin->used, 0) << ptr0 << ": ptr0" << std::endl << *libk::begin;
+ EXPECT_NE(begin->next, nullptr);
+ begin = begin->next;
+ EXPECT_EQ(begin->used, 1);
+ EXPECT_NE(begin->next, nullptr);
+ begin = begin->next;
+ EXPECT_EQ(begin->used, 0);
+ EXPECT_EQ(begin->next, nullptr);
+
+ libk::free(ptr1);
+ begin = libk::begin;
+ EXPECT_EQ(begin->used, 0) << ptr0 << ": ptr0" << std::endl << *libk::begin;
+ EXPECT_EQ(begin->next, nullptr);
+}
diff --git a/lib/libk/stdlib/test_mem.cc b/lib/libk/stdlib/test_mem.cc
new file mode 100644
index 0000000..f8a5e18
--- /dev/null
+++ b/lib/libk/stdlib/test_mem.cc
@@ -0,0 +1,29 @@
+#include <gtest/gtest.h>
+
+#define restrict __restrict__
+
+namespace libk {
+#include "memcpy.c"
+#include "memset.c"
+} // namespace libk
+
+TEST(mem, memset)
+{
+ auto *buffer = new unsigned char[2048];
+
+ libk::memset(buffer, 0xae, sizeof(buffer));
+ for (unsigned i = 0; i < sizeof(buffer); ++i) EXPECT_EQ(buffer[i], 0xae);
+
+ delete[] buffer;
+}
+
+TEST(mem, memcpy)
+{
+ const unsigned char data[] = {0xde, 0xca, 0xfa, 0xde};
+ auto *buffer = new unsigned char[sizeof(data)];
+
+ memcpy(buffer, data, sizeof(data));
+ for (unsigned i = 0; i < sizeof(data); ++i) EXPECT_EQ(buffer[i], data[i]);
+
+ delete[] buffer;
+}
diff --git a/lib/libk/string.h b/lib/libk/string.h
deleted file mode 100644
index c8196c8..0000000
--- a/lib/libk/string.h
+++ /dev/null
@@ -1,28 +0,0 @@
-#pragma once
-
-///@defgroup libk libk
-///@{
-///@defgroup string string
-///@{
-
-#define OCTAL 8
-#define DECIMAL 10
-#define HEX 16
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-/**
- * Convert int into a string
- */
-char *itoa(char *p, int x, int base);
-/**
- * Convert unsigned int into a string
- */
-char *utoa(char *p, unsigned x, int base);
-#ifdef __cplusplus
-}
-#endif
-
-///@}
-///@}
diff --git a/lib/libk/string/itoa.c b/lib/libk/string/itoa.c
index 2db9768..0997345 100644
--- a/lib/libk/string/itoa.c
+++ b/lib/libk/string/itoa.c
@@ -4,7 +4,7 @@
static const char *numbers = "0123456789abcdef";
char *
-utoa(char *p, unsigned x, int base)
+utoa(char *p, unsigned x, unsigned base)
{
p += 3 * sizeof(unsigned);
*--p = '\0';
@@ -18,12 +18,12 @@ utoa(char *p, unsigned x, int base)
}
char *
-itoa(char *p, int x, int base)
+itoa(char *p, int x, unsigned base)
{
const bool is_negative = (x < 0);
if (is_negative) x = -x;
- p = utoa(p, x, base);
+ p = utoa(p, (unsigned)x, base);
if (is_negative) *--p = '-';
return p;
diff --git a/lib/libk/string/test_string.cc b/lib/libk/string/test_string.cc
new file mode 100644
index 0000000..d12b318
--- /dev/null
+++ b/lib/libk/string/test_string.cc
@@ -0,0 +1,20 @@
+#include <gtest/gtest.h>
+
+namespace libk {
+#include "itoa.c"
+}
+
+char buffer[64];
+
+TEST(itoa, itoa)
+{
+ EXPECT_STREQ(libk::itoa(buffer, 12341234, 10), "12341234");
+ EXPECT_STREQ(libk::itoa(buffer, -12341234, 10), "-12341234");
+ EXPECT_STREQ(libk::itoa(buffer, 0x7fffffff, 16), "7fffffff");
+}
+
+TEST(itoa, utoa)
+{
+ EXPECT_STREQ(libk::utoa(buffer, 12341234u, 10), "12341234");
+ EXPECT_STREQ(libk::utoa(buffer, 0xdecafade, 16), "decafade");
+}