summaryrefslogtreecommitdiff
path: root/euler
diff options
context:
space:
mode:
authorBenji Dial <benji@benjidial.net>2024-05-18 21:53:38 -0400
committerBenji Dial <benji@benjidial.net>2024-05-18 21:53:38 -0400
commitb1a912a8a6ff472a49b2e0a09cfd433adfc2cb24 (patch)
tree5009d4415ba13e4baa37f3d0271852528130fd3b /euler
parenta8a80d326de9550b2a25b1255a2093ab43219ede (diff)
downloadhilbert-os-b1a912a8a6ff472a49b2e0a09cfd433adfc2cb24.tar.gz
reorganization, cross compiler
Diffstat (limited to 'euler')
-rw-r--r--euler/include/cstdio15
-rw-r--r--euler/include/cstring8
-rw-r--r--euler/include/euler/heap.hpp8
-rw-r--r--euler/include/euler/stream.hpp48
-rw-r--r--euler/include/euler/syscall.hpp61
-rw-r--r--euler/makefile29
-rw-r--r--euler/source/empty.asm0
-rw-r--r--euler/source/euler/entry.cpp11
-rw-r--r--euler/source/euler/gcc.asm53
-rw-r--r--euler/source/euler/heap.cpp66
-rw-r--r--euler/source/euler/stream.cpp151
-rw-r--r--euler/source/euler/syscall.asm102
-rw-r--r--euler/source/io/fclose.cpp10
-rw-r--r--euler/source/io/fopen.cpp54
-rw-r--r--euler/source/io/fread.cpp9
-rw-r--r--euler/source/memory/delete.cpp17
-rw-r--r--euler/source/memory/new.cpp13
-rw-r--r--euler/source/strings/memcpy.cpp14
-rw-r--r--euler/source/strings/strlen.cpp12
19 files changed, 681 insertions, 0 deletions
diff --git a/euler/include/cstdio b/euler/include/cstdio
new file mode 100644
index 0000000..27073db
--- /dev/null
+++ b/euler/include/cstdio
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <euler/stream.hpp>
+#include <stddef.h>
+
+namespace std {
+
+ typedef euler::stream FILE;
+
+ FILE *fopen(const char *filename, const char *mode);
+ int fclose(FILE *stream);
+
+ size_t fread(void *buffer, size_t size, size_t count, FILE *stream);
+
+}
diff --git a/euler/include/cstring b/euler/include/cstring
new file mode 100644
index 0000000..45bc94e
--- /dev/null
+++ b/euler/include/cstring
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stddef.h>
+
+namespace std {
+ size_t strlen(const char *str);
+ void *memcpy(void *dest, const void *src, size_t count);
+}
diff --git a/euler/include/euler/heap.hpp b/euler/include/euler/heap.hpp
new file mode 100644
index 0000000..e2a4852
--- /dev/null
+++ b/euler/include/euler/heap.hpp
@@ -0,0 +1,8 @@
+#pragma once
+
+#include <stdint.h>
+
+namespace euler {
+ void *alloc(uint64_t bytes);
+ void dealloc(void *start, uint64_t bytes);
+}
diff --git a/euler/include/euler/stream.hpp b/euler/include/euler/stream.hpp
new file mode 100644
index 0000000..a364ec4
--- /dev/null
+++ b/euler/include/euler/stream.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include <euler/syscall.hpp>
+
+namespace euler {
+
+ class stream {
+
+ public:
+ virtual ~stream();
+ virtual bool try_read(void *into, uint64_t bytes) = 0;
+ virtual bool try_seek(__euler_seek_from from, int64_t offset) = 0;
+
+ };
+
+ class file_stream : public stream {
+
+ private:
+
+ __euler_stream_handle handle;
+ bool is_readable;
+ bool is_writable;
+ uint64_t length;
+ uint64_t offset;
+
+ uint8_t *buffer;
+ uint64_t buffer_offset;
+ uint64_t buffer_size;
+ bool buffer_dirty;
+
+ void write_buffer();
+
+ public:
+
+ bool good;
+
+ file_stream(
+ __euler_stream_handle handle, bool is_readable, bool is_writable,
+ bool clear, bool seek_to_end);
+
+ ~file_stream();
+
+ bool try_read(void *into, uint64_t bytes) override;
+ bool try_seek(__euler_seek_from from, int64_t offset) override;
+
+ };
+
+}
diff --git a/euler/include/euler/syscall.hpp b/euler/include/euler/syscall.hpp
new file mode 100644
index 0000000..9255642
--- /dev/null
+++ b/euler/include/euler/syscall.hpp
@@ -0,0 +1,61 @@
+#pragma once
+
+#include <stdint.h>
+
+enum __euler_stream_result : uint64_t {
+ __EULER_SR_SUCCESS = 0,
+ __EULER_SR_BAD_HANDLE,
+ __EULER_SR_IO_ERROR,
+ __EULER_SR_OUT_OF_BOUNDS,
+ __EULER_SR_DOES_NOT_EXIST,
+ __EULER_SR_NOT_A_REGULAR_FILE,
+ __EULER_SR_NOT_AN_EXECUTABLE,
+ __EULER_SR_NOT_WRITABLE,
+ __EULER_SR_NOT_SEEKABLE,
+ __EULER_SR_SOCKET_ID_ALREADY_IN_USE,
+ __EULER_SR_SOCKET_ID_NOT_IN_USE,
+ __EULER_SR_SOCKET_LISTENER_CLOSED,
+ __EULER_SR_OTHER_END_CLOSED,
+ __EULER_SR_ALREADY_EXISTS,
+ __EULER_SR_NOT_SIZED
+};
+
+enum __euler_seek_from : uint8_t {
+ __EULER_SF_BEGINNING = 0,
+ __EULER_SF_END,
+ __EULER_SF_CURRENT_POSITION
+};
+
+typedef uint64_t __euler_stream_handle;
+
+extern "C" __euler_stream_result __euler_open_file(
+ const char *path, uint64_t path_len, __euler_stream_handle &handle_out,
+ bool allow_creation, bool only_allow_creation);
+
+extern "C" [[noreturn]] void __euler_end_this_thread(int32_t exit_code);
+
+extern "C" __euler_stream_result __euler_seek_stream(
+ __euler_stream_handle handle, __euler_seek_from from, int64_t offset);
+
+extern "C" __euler_stream_result __euler_set_stream_length(
+ __euler_stream_handle handle, uint64_t new_length);
+
+extern "C" void __euler_close_stream(__euler_stream_handle handle);
+
+extern "C" __euler_stream_result __euler_get_stream_length(
+ __euler_stream_handle handle, uint64_t &length_out);
+
+extern "C" void *__euler_get_new_pages(uint64_t count);
+
+extern "C" __euler_stream_result __euler_write_to_stream(
+ __euler_stream_handle handle, uint64_t count, const void *buffer);
+
+extern "C" __euler_stream_result __euler_read_from_stream(
+ __euler_stream_handle handle, uint64_t count, void *buffer);
+
+extern "C" uint32_t *__euler_get_framebuffer(
+ uint32_t &width_out, uint32_t &height_out, uint32_t &pitch_out);
+
+extern "C" uint32_t __euler_encode_color(uint8_t r, uint8_t g, uint8_t b);
+
+extern "C" uint32_t __euler_read_key_packet();
diff --git a/euler/makefile b/euler/makefile
new file mode 100644
index 0000000..3b8022d
--- /dev/null
+++ b/euler/makefile
@@ -0,0 +1,29 @@
+LIBSTDCPP_SOURCES = euler/stream.cpp strings/strlen.cpp euler/syscall.asm \
+ euler/entry.cpp io/fopen.cpp euler/gcc.asm memory/delete.cpp euler/heap.cpp \
+ memory/new.cpp io/fclose.cpp io/fread.cpp strings/memcpy.cpp
+
+clean:
+ rm -rf build
+
+build/%.asm.o: source/%.asm
+ @mkdir -p $(@D)
+ $(HILBERT_NASM) $^ -o $@
+
+build/%.cpp.o: source/%.cpp
+ @mkdir -p $(@D)
+ $(HILBERT_CC) -c $^ -o $@
+
+build/crt0.o: build/empty.asm.o
+ cp $^ $@
+
+build/libc.a: build/empty.asm.o
+ $(HILBERT_AR) rcs $@ $^
+
+build/libg.a: build/empty.asm.o
+ $(HILBERT_AR) rcs $@ $^
+
+build/libm.a: build/empty.asm.o
+ $(HILBERT_AR) rcs $@ $^
+
+build/libstdc++.a: ${LIBSTDCPP_SOURCES:%=build/%.o}
+ $(HILBERT_AR) rcs $@ $^
diff --git a/euler/source/empty.asm b/euler/source/empty.asm
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/euler/source/empty.asm
diff --git a/euler/source/euler/entry.cpp b/euler/source/euler/entry.cpp
new file mode 100644
index 0000000..69611b7
--- /dev/null
+++ b/euler/source/euler/entry.cpp
@@ -0,0 +1,11 @@
+#include <euler/syscall.hpp>
+
+int main(int argc, char **argv);
+
+extern "C" [[noreturn]] void _start() {
+ //TODO: call global constructors
+ //TODO: get argc, argv from environment
+ int exit_code = main(0, 0);
+ //TODO: call global destructors
+ __euler_end_this_thread(exit_code);
+}
diff --git a/euler/source/euler/gcc.asm b/euler/source/euler/gcc.asm
new file mode 100644
index 0000000..6fc6fd5
--- /dev/null
+++ b/euler/source/euler/gcc.asm
@@ -0,0 +1,53 @@
+bits 64
+
+global strlen
+
+section .text
+
+strlen:
+ xor rax, rax
+.loop:
+ mov rdx, qword [rdi]
+ test edx, 0xff
+ jz .plus0
+ test edx, 0xff00
+ jz .plus1
+ test edx, 0xff0000
+ jz .plus2
+ test edx, 0xff000000
+ jz .plus3
+ shr rdx, 32
+ test edx, 0xff
+ jz .plus4
+ test edx, 0xff00
+ jz .plus5
+ test edx, 0xff0000
+ jz .plus6
+ test edx, 0xff000000
+ jz .plus7
+ add rax, 8
+ add rdi, 8
+ jmp .loop
+.plus0:
+ ret
+.plus1:
+ add rax, 1
+ ret
+.plus2:
+ add rax, 2
+ ret
+.plus3:
+ add rax, 3
+ ret
+.plus4:
+ add rax, 4
+ ret
+.plus5:
+ add rax, 5
+ ret
+.plus6:
+ add rax, 6
+ ret
+.plus7:
+ add rax, 7
+ ret
diff --git a/euler/source/euler/heap.cpp b/euler/source/euler/heap.cpp
new file mode 100644
index 0000000..f7d407f
--- /dev/null
+++ b/euler/source/euler/heap.cpp
@@ -0,0 +1,66 @@
+#include <euler/syscall.hpp>
+#include <euler/heap.hpp>
+
+namespace euler {
+
+ static uint64_t *last_chunk_info_page = 0;
+
+ void add_record(uint64_t start, uint64_t length) {
+ uint64_t dual_start = start ^ length;
+ for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510])
+ for (int i = 0; i < 255; ++i)
+ if (cip[i * 2] == length && cip[i * 2 + 1] == dual_start) {
+ cip[i * 2] = length * 2;
+ cip[i * 2 + 1] &= start;
+ return;
+ }
+ for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510])
+ for (int i = 0; i < 255; ++i)
+ if (cip[i * 2] == 0) {
+ cip[i * 2] = length;
+ cip[i * 2 + 1] = start;
+ return;
+ }
+ uint64_t *new_cip = (uint64_t *)__euler_get_new_pages(1);
+ new_cip[0] = length;
+ new_cip[1] = start;
+ for (int i = 1; i < 255; ++i)
+ new_cip[i * 2] = 0;
+ new_cip[510] = (uint64_t)last_chunk_info_page;
+ last_chunk_info_page = new_cip;
+ }
+
+ void *alloc(uint64_t bytes) {
+ for (uint64_t *cip = last_chunk_info_page; cip; cip = (uint64_t *)cip[510])
+ for (int i = 0; i < 255; ++i)
+ if (cip[i * 2] >= bytes) {
+ uint64_t start = cip[i * 2 + 1];
+ uint64_t extra_length = cip[i * 2] - bytes;
+ cip[i * 2] = 0;
+ if (extra_length > 0)
+ dealloc((void *)(start + bytes), extra_length);
+ return (void *)start;
+ }
+ uint64_t pages_needed = (bytes - 1) / 4096 + 1;
+ uint64_t start = (uint64_t)__euler_get_new_pages(pages_needed);
+ if (bytes != pages_needed * 4096)
+ dealloc((void *)(start + bytes), pages_needed * 4096 - bytes);
+ return (void *)start;
+ }
+
+ //this implementation is a little lazy
+ void dealloc(void *start, uint64_t bytes) {
+ uint64_t s = (uint64_t)start;
+ while (bytes > 0) {
+ uint64_t l = 1;
+ do
+ l *= 2;
+ while (l <= bytes && s % l == 0);
+ l /= 2;
+ add_record(s, l);
+ s += l;
+ bytes -= l;
+ }
+ }
+
+}
diff --git a/euler/source/euler/stream.cpp b/euler/source/euler/stream.cpp
new file mode 100644
index 0000000..950f3c5
--- /dev/null
+++ b/euler/source/euler/stream.cpp
@@ -0,0 +1,151 @@
+#include <euler/stream.hpp>
+#include <cstring>
+
+namespace euler {
+
+ stream::~stream() {}
+
+ void file_stream::write_buffer() {
+ if (__euler_write_to_stream(handle, buffer_size, buffer)
+ != __EULER_SR_SUCCESS)
+ good = false;//TODO: more precise error reporting
+ else
+ buffer_dirty = false;
+ }
+
+ file_stream::file_stream(
+ __euler_stream_handle handle, bool is_readable, bool is_writable,
+ bool clear, bool seek_to_end)
+
+ : handle(handle), is_readable(is_readable), is_writable(is_writable),
+ buffer(0), good(true) {
+
+ if (clear) {
+ if (__euler_set_stream_length(handle, 0) != __EULER_SR_SUCCESS) {
+ good = false;
+ return;
+ }
+ length = 0;
+ }
+
+ else if (__euler_get_stream_length(handle, length) != __EULER_SR_SUCCESS) {
+ good = false;
+ return;
+ }
+
+ if (seek_to_end) {
+ if (__euler_seek_stream(handle, __EULER_SF_END, 0)
+ != __EULER_SR_SUCCESS) {
+ good = false;
+ return;
+ }
+ offset = length;
+ }
+
+ else
+ offset = 0;
+
+ }
+
+ file_stream::~file_stream() {
+ if (buffer) {
+ if (buffer_dirty)
+ write_buffer();
+ delete[] buffer;
+ }
+ __euler_close_stream(handle);
+ }
+
+ bool file_stream::try_read(void *into, uint64_t bytes) {
+
+ if (bytes == 0)
+ return true;
+
+ if (offset + bytes > length)
+ return false;
+
+ if (buffer) {
+ uint64_t start = offset > buffer_offset ? offset : buffer_offset;
+ uint64_t end = offset + bytes < buffer_offset + buffer_size
+ ? offset + bytes : buffer_offset + buffer_size;
+ if (end > start) {
+
+ uint64_t real_end = offset + bytes;
+
+ std::memcpy(
+ (uint8_t *)into + start - offset, buffer + start - buffer_offset,
+ end - start);
+ if (start != offset)
+ if (!try_read(into, start - offset))
+ return false;
+
+ if (end != real_end) {
+ if (!try_seek(__EULER_SF_BEGINNING, end))
+ return false;
+ if (!try_read((uint8_t *)into + end, real_end - end))
+ return false;
+ }
+
+ else if (offset != real_end)
+ if (!try_seek(__EULER_SF_BEGINNING, real_end))
+ return false;
+
+ return true;
+
+ }
+ }
+
+ if (buffer_dirty) {
+ write_buffer();
+ if (!good)
+ return false;
+ }
+
+ //1024 is arbitrary
+ buffer_size = length - offset < 1024 ? length - offset : 1024;
+
+ if (buffer != 0)
+ delete[] buffer;
+
+ buffer = new uint8_t[buffer_size];
+ buffer_dirty = false;
+ buffer_offset = offset;
+
+ if (__euler_read_from_stream(handle, buffer_size, buffer)
+ != __EULER_SR_SUCCESS) {
+ delete[] buffer;
+ buffer = 0;
+ return false;
+ }
+
+ uint64_t read_this_buffer = buffer_size < bytes ? buffer_size : bytes;
+ std::memcpy(into, buffer, read_this_buffer);
+ offset += read_this_buffer;
+
+ if (read_this_buffer == bytes)
+ return true;
+
+ return try_read(
+ (uint8_t *)into + read_this_buffer, bytes - read_this_buffer);
+
+ }
+
+ bool file_stream::try_seek(__euler_seek_from from, int64_t offset) {
+ if (__euler_seek_stream(handle, from, offset) != __EULER_SR_SUCCESS)
+ return false;
+ switch (from) {
+ case __EULER_SF_BEGINNING:
+ this->offset = offset;
+ return true;
+ case __EULER_SF_END:
+ this->offset = length + offset;
+ return true;
+ case __EULER_SF_CURRENT_POSITION:
+ this->offset += offset;
+ return true;
+ default:
+ return false;
+ }
+ }
+
+}
diff --git a/euler/source/euler/syscall.asm b/euler/source/euler/syscall.asm
new file mode 100644
index 0000000..34f2735
--- /dev/null
+++ b/euler/source/euler/syscall.asm
@@ -0,0 +1,102 @@
+bits 64
+
+global __euler_open_file
+global __euler_close_stream
+global __euler_get_new_pages
+global __euler_write_to_stream
+global __euler_seek_stream
+global __euler_get_stream_length
+global __euler_set_stream_length
+global __euler_end_this_thread
+global __euler_read_from_stream
+global __euler_get_framebuffer
+global __euler_encode_color
+global __euler_read_key_packet
+
+section .text
+
+__euler_open_file:
+ mov rax, 2
+ push rdx
+ xor rdx, rdx
+ or rdx, r8
+ shl r9, 1
+ or rdx, r9
+ syscall
+ pop rdx
+ mov qword [rdx], rdi
+ ret
+
+__euler_close_stream:
+ mov rax, 11
+ syscall
+ ret
+
+__euler_get_new_pages:
+ mov rax, 4
+ syscall
+ ret
+
+__euler_write_to_stream:
+ mov rax, 14
+ syscall
+ ret
+
+__euler_seek_stream:
+ mov rax, 12
+ syscall
+ ret
+
+__euler_get_stream_length:
+ push rsi
+ mov rax, 15
+ syscall
+ pop rsi
+ mov qword [rsi], rdi
+ ret
+
+__euler_set_stream_length:
+ mov rax, 18
+ syscall
+ ret
+
+__euler_end_this_thread:
+ mov rax, 3
+ syscall
+
+__euler_read_from_stream:
+ mov rax, 13
+ syscall
+ ret
+
+__euler_get_framebuffer:
+ push rdi
+ push rsi
+ push rdx
+ mov rax, 1
+ syscall
+ pop rdx
+ mov dword [rdx], esi
+ pop rdx
+ pop rsi
+ mov dword [rsi], edi
+ shr rdi, 32
+ mov dword [rdx], edi
+ ret
+
+__euler_encode_color:
+ xor ah, ah
+ mov al, dl
+ shl ax, 8
+ mov al, sil
+ shl eax, 8
+ mov al, dil
+ mov edi, eax
+ xor rax, rax
+ syscall
+ ret
+
+__euler_read_key_packet:
+ mov rax, 5
+ syscall
+ ret
diff --git a/euler/source/io/fclose.cpp b/euler/source/io/fclose.cpp
new file mode 100644
index 0000000..6f43f85
--- /dev/null
+++ b/euler/source/io/fclose.cpp
@@ -0,0 +1,10 @@
+#include <cstdio>
+
+namespace std {
+
+ int fclose(FILE *stream) {
+ delete stream;
+ return 0;
+ }
+
+}
diff --git a/euler/source/io/fopen.cpp b/euler/source/io/fopen.cpp
new file mode 100644
index 0000000..8d47bf0
--- /dev/null
+++ b/euler/source/io/fopen.cpp
@@ -0,0 +1,54 @@
+#include <stdint.h>
+#include <cstring>
+#include <cstdio>
+
+namespace std {
+
+ FILE *fopen(const char *filename, const char *mode) {
+
+ bool read = false;
+ bool write = false;
+ bool append = false;
+ bool extended = false;
+ bool create = false;
+
+ for (const char *p = mode; *p; ++p)
+ switch (*p) {
+ case 'r':
+ read = true;
+ continue;
+ case 'w':
+ write = true;
+ continue;
+ case 'a':
+ append = true;
+ continue;
+ case '+':
+ extended = true;
+ continue;
+ case 'x':
+ create = true;
+ continue;
+ default:
+ continue;
+ }
+
+ __euler_stream_handle handle;
+ __euler_stream_result res = __euler_open_file(
+ filename, strlen(filename), handle, write || append, create);
+
+ if (res != __EULER_SR_SUCCESS)
+ return 0;
+
+ euler::file_stream *f = new euler::file_stream(handle, read || extended,
+ write || extended, write && !append, append);
+
+ if (f->good)
+ return f;
+
+ delete f;
+ return 0;
+
+ }
+
+}
diff --git a/euler/source/io/fread.cpp b/euler/source/io/fread.cpp
new file mode 100644
index 0000000..e2d05b6
--- /dev/null
+++ b/euler/source/io/fread.cpp
@@ -0,0 +1,9 @@
+#include <cstdio>
+
+namespace std {
+
+ size_t fread(void *buffer, size_t size, size_t count, FILE *stream) {
+ return stream->try_read(buffer, size * count) ? count : 0;
+ }
+
+}
diff --git a/euler/source/memory/delete.cpp b/euler/source/memory/delete.cpp
new file mode 100644
index 0000000..e4cc288
--- /dev/null
+++ b/euler/source/memory/delete.cpp
@@ -0,0 +1,17 @@
+#include <euler/heap.hpp>
+
+void operator delete(void *ptr) {
+ euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8));
+}
+
+void operator delete(void *ptr, uint64_t) {
+ euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8));
+}
+
+void operator delete[](void *ptr) {
+ euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8));
+}
+
+void operator delete[](void *ptr, uint64_t) {
+ euler::dealloc((uint8_t *)ptr - 8, *(uint64_t *)((uint8_t *)ptr - 8));
+}
diff --git a/euler/source/memory/new.cpp b/euler/source/memory/new.cpp
new file mode 100644
index 0000000..931328f
--- /dev/null
+++ b/euler/source/memory/new.cpp
@@ -0,0 +1,13 @@
+#include <euler/heap.hpp>
+
+void *operator new(uint64_t size) {
+ void *ptr = euler::alloc(size + 8);
+ *(uint64_t *)ptr = size + 8;
+ return (uint8_t *)ptr + 8;
+}
+
+void *operator new[](uint64_t size) {
+ void *ptr = euler::alloc(size + 8);
+ *(uint64_t *)ptr = size + 8;
+ return (uint8_t *)ptr + 8;
+}
diff --git a/euler/source/strings/memcpy.cpp b/euler/source/strings/memcpy.cpp
new file mode 100644
index 0000000..d5a1d6c
--- /dev/null
+++ b/euler/source/strings/memcpy.cpp
@@ -0,0 +1,14 @@
+#include <stdint.h>
+#include <cstring>
+
+namespace std {
+
+ void *memcpy(void *dest, const void *src, size_t count) {
+ uint8_t *d8 = (uint8_t *)dest;
+ const uint8_t *s8 = (const uint8_t *)src;
+ for (size_t i = 0; i < count; ++i)
+ d8[i] = s8[i];
+ return dest;
+ }
+
+}
diff --git a/euler/source/strings/strlen.cpp b/euler/source/strings/strlen.cpp
new file mode 100644
index 0000000..7a6fe2a
--- /dev/null
+++ b/euler/source/strings/strlen.cpp
@@ -0,0 +1,12 @@
+#include <cstring>
+
+namespace std {
+
+ size_t strlen(const char *str) {
+ size_t len = 0;
+ while (str[len])
+ ++len;
+ return len;
+ }
+
+}