From 8eeb7e0cf89ed9e8cf68bbb3ba23556204d9075a Mon Sep 17 00:00:00 2001 From: Thomas Walker Lynch Date: Tue, 25 Feb 2025 15:04:41 +0000 Subject: [PATCH] checkpoint --- .../Python\360\237\226\211/template_N.py" | 4 +- "developer/cc\360\237\226\211/Copy.lib.c" | 229 ++++++++++++++- "developer/document\360\237\226\211/Copy.org" | 72 +++++ "developer/document\360\237\226\211/todo.org" | 19 ++ release/x86_64/fedora41/glibc_2.40/Copy.lib.c | 152 ++++++++++ release/x86_64/fedora41/glibc_2.40/N16.lib.c | 6 +- release/x86_64/fedora41/glibc_2.40/N32.lib.c | 6 +- .../x86_64/fedora41/glibc_2.40/N32_1x32.lib.c | 250 ++++++++++------ .../x86_64/fedora41/glibc_2.40/N32_4x8.lib.c | 6 +- release/x86_64/fedora41/glibc_2.40/N64.lib.c | 6 +- release/x86_64/fedora41/glibc_2.40/N8.lib.c | 6 +- release/x86_64/fedora41/glibc_2.40/libN.a | Bin 17462 -> 17456 bytes tester/cc/Copy.lib.c | 152 ++++++++++ tester/cc/N16PN.lib.c | 6 +- tester/cc/N32PN.lib.c | 6 +- tester/cc/N32_1x32.lib.c | 242 +++++++++++----- tester/cc/N64PN.lib.c | 6 +- tester/cc/N8PN.lib.c | 6 +- "tester/cc\360\237\226\211/test_Copy_0.cli.c" | 269 ++++++++++++++++++ "tester/cc\360\237\226\211/test_setup.cli.c" | 7 +- "tester/tool\360\237\226\211/release_pull" | 14 +- "tester/tool\360\237\226\211/release_remove" | 8 +- 22 files changed, 1264 insertions(+), 208 deletions(-) create mode 100644 "developer/document\360\237\226\211/Copy.org" create mode 100644 release/x86_64/fedora41/glibc_2.40/Copy.lib.c create mode 100644 tester/cc/Copy.lib.c create mode 100644 "tester/cc\360\237\226\211/test_Copy_0.cli.c" diff --git "a/developer/Python\360\237\226\211/template_N.py" "b/developer/Python\360\237\226\211/template_N.py" index 0c0ef0c..0d51033 100644 --- "a/developer/Python\360\237\226\211/template_N.py" +++ "b/developer/Python\360\237\226\211/template_N.py" @@ -153,7 +153,7 @@ def template_N(): NS·Status (*arithmetic_shift_right)(Address, NS·T*, NS·T*); // import/export - char *(*to_string)(NS·T *); + char *(*to_hex)(NS·T *); #ifdef UINT8_MAX NS·TO_TYPE(uint8_t) @@ -223,7 +223,7 @@ def template_N(): free(unencumbered); } - char *to_string(NS·T *n) { + char *to_hex(NS·T *n) { // Each byte requires two hex characters, plus "0x" prefix and null terminator const Address string_length = (sizeof(Digit) * (digit_array_extent + 1) * 2) + 3; char *buffer = malloc(string_length); diff --git "a/developer/cc\360\237\226\211/Copy.lib.c" "b/developer/cc\360\237\226\211/Copy.lib.c" index 2563ec0..4e40437 100644 --- "a/developer/cc\360\237\226\211/Copy.lib.c" +++ "b/developer/cc\360\237\226\211/Copy.lib.c" @@ -1,6 +1,7 @@ /* Copy - Memory copy operations with attention to alignment. Provides optimized copy and byte order reversal functions. + */ #define Copy·DEBUG @@ -19,8 +20,27 @@ #include #include - void *Copy·region(void *read0 ,void *read1 ,void *write0); - void *Copy·reverse_byte_order(void *read0 ,void *read1 ,void *write0); + typedef struct{ + void *read0; + size_t read_size; + void *write0; + size_t write_size; + } Copy·it; + + typedef enum{ + Copy·Status·perfect_fit = 0 + ,Copy·Status·argument_guard + ,Copy·Status·read_surplus + ,Copy·Status·read_surplus_write_gap + ,Copy·Status·write_available + ,Copy·Status·write_gap // write allocation has a terminal gap + } Copy·Status; + + typedef struct{ + void *region(void *read0 ,void *read1 ,void *write0); + void *reverse_byte_order(void *read0 ,void *read1 ,void *write0); + } Copy·M; + #endif @@ -35,13 +55,45 @@ #ifdef LOCAL + + + + #ifdef Copy·DEBUG + #include // Only for debug prints, not used in production. + +typedef enum{ + Copy·StatusWFIt·none = 0x00 + ,Copy·StatusWFIt·null_read = 0x01 + ,Copy·StatusWFIt·null_write = 0x02 + ,Copy·StatusWFIt·zero_read_size = 0x04 + ,Copy·StatusWFIt·zero_write_size = 0x08 + ,Copy·StatusWFIt·write_too_small_hex = 0x10 + ,Copy·StatusWFIt·read_too_small_hex = 0x20 + ,Copy·StatusWFIt·read_larger_than_write = 0x40 + ,Copy·StatusWFIt·overlapping_buffers = 0x80 +} Copy·StatusWFIt; + +typedef enum{ + Copy·ModeWFIt·none = 0x00 + ,Copy·ModeWFIt·bytes = 0x01 + ,Copy·ModeWFIt·reverse = 0x02 + ,Copy·ModeWFIt·write_hex = 0x03 + ,Copy·ModeWFIt·from_hex = 0x04 +} Copy·ModeWFIt; + + + + +#endif + + /* Copy·region - Copies a memory region while preserving byte order. - Aligns reads for performance. - Writes are assumed to be buffered and do not require alignment. - Returns the updated write pointer. */ - void *Copy·region(void *read0 ,void *read1 ,void *write0){ + Local void *Copy·bytes(void *read0 ,void *read1 ,void *write0){ uint8_t *r = (uint8_t *)read0; uint8_t *r1 = (uint8_t *)read1; @@ -93,11 +145,12 @@ /* Copy·reverse_byte_order - Copies a memory region while reversing byte order. - - Reads in reverse order while writing in forward order. + - Reads from read1 down + - writes from write0 up - Uses `__builtin_bswap64` for efficient 64-bit swaps. - Returns the updated write pointer. */ - void *Copy·reverse_byte_order(void *read0 ,void *read1 ,void *write0){ + Local void *Copy·bytes_reverse_order(void *read0 ,void *read1 ,void *write0){ uint8_t *r = (uint8_t *)read1; // Start from the last byte uint8_t *r0 = (uint8_t *)read0; @@ -147,6 +200,172 @@ return w; } + /* + Read buffer is read from the lowest address, working toward higher addresses. + + Write buffer is written from the lowest address, working to higher addresses. + + To force data to be left in the read buffer, or for capacity to be left in the + write buffer, reduce sizes. + */ + Local Copy·Status Copy·step( + Copy·it *it + ){ + uint8_t *r = (uint8_t *)it->read0; + uint8_t *w = (uint8_t *)it->write0; + + size_t rs = it->read_size; + size_t ws = it->write_size; + + if(ws >= rs){ + Copy·bytes(r ,r + rs ,w); + it->read0 += rs; + it->read_size = 0; + it->write0 += rs; + it->write_size -= rs; + if(ws == rs) return Copy·Status·perfect_fit; + return Copy·Status·write_available;; + } + + // ws < rs + Copy·bytes(r ,r + ws ,w); + it->read0 += ws; + it->read_size -= ws; + it->write_size = 0; + it->write0 += ws; + return Copy·Status·read_surplus; + } + + /* + Read buffer is read from top down. Start with the largest address + just above the read buffer. Continue into lower addresses. + + write buffer is written from bottom up. Start with the lowest address, + continue into higher addresses. + */ + Local Copy·Status Copy·step_reverse_order(Copy·it *it){ + // How many bytes remain to be read/written + if( it->read_size == 0) return Copy·Status·complete; + size_t rs = it->read_size; + uint8_t *r1 = (uint8_t *)it->read0 + rs; + size_t ws = it->write_size; + uint8_t *w0 = (uint8_t *)it->write0; + + if(ws >= rs){ + uint8_t *r0 = (uint8_t *)it->read0; + Copy·bytes_reverse_order(r0, r1, w0); + it->read_size = 0; + it->write0 += rs; + it->write_size -= rs; + if(it->write_size == 0) return Copy·Status·perfect_fit; + return Copy·Status·write_available; + } + + // ws < rs + uint8_t *r0 = r1 - ws; + Copy·bytes_reverse_order(r0, r1, w0); + it->read0 -= ws; + it->read_size -= ws; + it->write_size = 0; + it->write0 += ws; + return Copy·Status·read_surplus; + } + + /* + Read bytes, write hex pairs. + Read and write are low address to high address. + Each read byte value -> 2 write allocation bytes + */ + Local Copy·Status Copy·step_write_hex( + Copy·it *it + ){ + + uint8_t *r = (uint8_t *)it->read0; + size_t rs = it->read_size; + + uint8_t *w = (uint8_t *)it->write0; + size_t ws = it->write_size & ~1; // even number write_size + size_t ews = it->write_size >> 1; // effective write size + + // If ews >= rs, read bytes all coped + if(ews >= rs){ + size_t ers = it->read_size << 1; // effective read size + it->write0 += ers; + it->write_size -= ers; + while(rs--){ + *(uint16_t *)w = Copy·byte_to_hex(*r++); + w += 2; + } + it->read0 = r; + it->read_size = 0; + + if(it->write_size == 0) return Copy·Status·perfect_fit; + if(it->write_size == 1) return Copy·Status·write_gap; + return Copy·Status·write_available; + } + + // ews < rs, write allocation all used, read bytes surplus + it->read0 += ews; + it->read_size -= ews; + while(ews--){ + *(uint16_t *)w = Copy·byte_to_hex(*r++); + w += 2; + } + it->write0 = w; + it->write_size -= ws; + + if(it->write_size == 1) return Copy·Status·read_surplus_write_gap; + return Copy·Status·read_surplus; + } + + /* + Read hex pairs, write bytes. + Read is low address to high address. + Write is low address to high address. + Each read hex pair -> 1 write byte. + */ + Local Copy·Status Copy·step_from_hex( + Copy·it *it + ){ + uint8_t *r = (uint8_t *)it->read0; + size_t rs = it->read_size & ~1; // Must be even for hex pairs. + size_t ers = rs >> 1; // Effective read size: half the number of bytes. + + uint8_t *w = (uint8_t *)it->write0; + size_t ws = it->write_size; // Write size already in bytes. + + // If ws >= ers, all hex values are processed + if(ws >= ers){ + while(ers--){ + *w++ = Copy·hex_to_byte(*(uint16_t *)r); + r += 2; + } + + it->read0 = r; + it->read_size -= rs; + it->write0 = w; + it->write_size -= rs >> 1; // Each byte consumes two hex chars. + + if(it->write_size == 0) return Copy·Status·perfect_fit; + return Copy·Status·write_available; + } + + // ws < ers, read allocation surplus + while(ws--){ + *w++ = Copy·hex_to_byte(*(uint16_t *)r); + r += 2; + } + + it->read0 = r; + it->read_size -= ws << 1; // Each write byte consumes two hex chars. + it->write0 = w; + it->write_size = 0; + + return Copy·Status·read_surplus; + } + + #endif // LOCAL + #endif // IMPLEMENTATION diff --git "a/developer/document\360\237\226\211/Copy.org" "b/developer/document\360\237\226\211/Copy.org" new file mode 100644 index 0000000..38b9729 --- /dev/null +++ "b/developer/document\360\237\226\211/Copy.org" @@ -0,0 +1,72 @@ +* Copy - Memory Copy Operations + +** Overview + +The Copy module provides optimized memory copy operations that account for memory alignment and byte order. It contains functions for standard memory copying, as well as specialized routines for reversing byte order during copying. The design ensures efficient memory operations by aligning read pointers and assuming write operations are buffered. + +** Features + +- Optimized memory copy operations +- Byte order reversal during copy +- Handles unaligned read memory +- Ensures efficient bulk transfers using 64-bit word copies +- Provides step-based copying with status reporting + +** Interface + +- `Copy·bytes(read0, read1, write0) -> void*` + - Copies memory from `read0` to `write0`, preserving byte order. + - Returns the updated write pointer. + +- `Copy·reverse_byte_order(read0, read1, write0) -> void*` + - Copies memory while reversing byte order. + - Reads from `read1` downward, writes from `write0` upward. + - Returns the updated write pointer. + +- `Copy·step(Copy·it *it) -> Copy·Status` + - Uses the `Copy·it` struct, which defines read and write intervals. + - Does the maximum sized copy without going outside the intervals. + - Updates `it` for continuation of the block copy. + - Returns a status indicating there is surplus data, or available write allocation. + +- `Copy·step_reverse_order(Copy·it *it) -> Copy·Status` + - Performs a copy of the block while reversing the byte order and updating the iterator. + - Returns a status indicating there is surplus data, or available write allocation. + +- `Copy·step_write_hex(Copy·it *it) -> Copy·Status` + - Converts read bytes into hexadecimal representation during writing. + - Two bytes are written for each byte read.buffer. + - Returns a status indicating there is surplus data, or available write allocation. + +** Status Codes + +- `Copy·Status·perfect_fit` + - Read and write buffers are fully utilized without remainder. +- `Copy·Status·read_data_surplus` + - The write allocation was filled before the read values were all read. +- `Copy·Status·write_allocation_available` + - The write allocation has space for more values. +- `Copy·Status·write_allocation_gap` + - The write allocation has space, but it is not large enough to hold another value. + +** Implementation Notes + +- Alignment is only enforced on the read side to maximize efficiency. +- Writes are assumed to be buffered, allowing unaligned writes without performance degradation. +- The `step` functions allow for incremental processing of data streams. + +** Example Usage + +```c +Copy·it copy_instance = { + .read0 = source_data, + .read_size = source_size, + .write0 = destination_buffer, + .write_size = destination_size +}; + +Copy·Status result = Copy·step(©_instance); +``` + +This module provides an efficient and flexible memory copying framework suitable for low-level data manipulation tasks. + diff --git "a/developer/document\360\237\226\211/todo.org" "b/developer/document\360\237\226\211/todo.org" index a63c4b6..984f156 100644 --- "a/developer/document\360\237\226\211/todo.org" +++ "b/developer/document\360\237\226\211/todo.org" @@ -10,3 +10,22 @@ done copy version that works when there is not even a unint32_t type. +* 2025-02-24T08:09:57Z Copy.lib.c forced alginment machine might have issues with the block copy. + + The block copy aligns the read pointer by copying some initial + bytes. It ignores the alignment on the write pointer. Then at the end it does a byte + by byte copy of the ragged tail (less than a full word number of bytes). + + For a system that forces alignment, the initial alignment of the read pointer will get skipped. The write pointer will be aligned, so there is no problem in not checking it. + + However, the ragged tail loop can fire on a forced aligned + system. This will happen if the bounding read pointer passed in to + the block copy is not pointing to the first byte of a word. This can + happen if it is created adding `sizeof` of an object with that is not an even number + of bytes in a word long. + + The solution is probably to set a 'force align' macro based on the + architecture macro and gating the ragged tail code to do a word + copy, or affecting the bulk section to do one more loop -- or having + a differnt block copy an bytes_reversed block copy that loops on + words. diff --git a/release/x86_64/fedora41/glibc_2.40/Copy.lib.c b/release/x86_64/fedora41/glibc_2.40/Copy.lib.c new file mode 100644 index 0000000..2563ec0 --- /dev/null +++ b/release/x86_64/fedora41/glibc_2.40/Copy.lib.c @@ -0,0 +1,152 @@ +/* + Copy - Memory copy operations with attention to alignment. + Provides optimized copy and byte order reversal functions. +*/ + +#define Copy·DEBUG + +#ifndef FACE +#define Copy·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef Copy·FACE +#define Copy·FACE + + #include + #include + + void *Copy·region(void *read0 ,void *read1 ,void *write0); + void *Copy·reverse_byte_order(void *read0 ,void *read1 ,void *write0); + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef Copy·IMPLEMENTATION + + // this part goes into Nlib.a + #ifndef LOCAL + #endif + + #ifdef LOCAL + + /* + Copy·region - Copies a memory region while preserving byte order. + - Aligns reads for performance. + - Writes are assumed to be buffered and do not require alignment. + - Returns the updated write pointer. + */ + void *Copy·region(void *read0 ,void *read1 ,void *write0){ + + uint8_t *r = (uint8_t *)read0; + uint8_t *r1 = (uint8_t *)read1; + uint8_t *w = (uint8_t *)write0; + + //---------- + // The potentially unaligned initial part (align read pointer). + if( (uintptr_t)r & 0x7 ){ + + // ORing in `0x7` adds at most six bytes to r. + uint8_t *r01 = (uint8_t *)((uintptr_t)r | 0x7); + + // If the read interval is very small + if(r01 >= r1){ + while(r < r1){ + *w++ = *r++; + } + return w; + } + + // Copy up to alignment boundary + do{ + *w++ = *r++; + }while(r <= r01); + } + // r is now aligned, but *r has not yet been copied + + //---------- + // The bulk copy part (w is still possibly unaligned, but r is aligned) + uint8_t *r10 = (uint8_t *)((uintptr_t)r1 & ~(uintptr_t)0x7); + + while(r < r10){ + *(uint64_t *)w = *(uint64_t *)r; + w += 8; + r += 8; + } + + // If r1 was aligned then r10 == r1 and we are done + if(r == r1) return w; + + //---------- + // The ragged tail, up to 7 bytes + do{ + *w++ = *r++; + }while(r < r1); + + return w; + } + + /* + Copy·reverse_byte_order - Copies a memory region while reversing byte order. + - Reads in reverse order while writing in forward order. + - Uses `__builtin_bswap64` for efficient 64-bit swaps. + - Returns the updated write pointer. + */ + void *Copy·reverse_byte_order(void *read0 ,void *read1 ,void *write0){ + + uint8_t *r = (uint8_t *)read1; // Start from the last byte + uint8_t *r0 = (uint8_t *)read0; + uint8_t *w = (uint8_t *)write0; + + //---------- + // The potentially unaligned initial part (align read pointer). + if( (uintptr_t)r & 0x7 ){ + + // ANDing with `~0x7` moves it downward to the nearest lower alignment. + uint8_t *r10 = (uint8_t *)((uintptr_t)r & ~(uintptr_t)0x7); + + // If the read interval is very small + if(r10 < r0){ + while(r > r0){ + *w++ = *--r; + } + return w; + } + + // Copy down to alignment boundary + do{ + *w++ = *--r; + }while(r > r10); + } + // r is now aligned, and *r has been copied + + //---------- + // The bulk copy part + uint8_t *r01 = (uint8_t *)( ((uintptr_t)r0 + (uintptr_t)0x7) & ~(uintptr_t)0x7); + + while(r > r01){ + r -= 8; + *(uint64_t *)w = __builtin_bswap64(*(uint64_t *)r); + w += 8; + } + + // If r0 was aligned then r01 == r0 and we are done + if(r < r0) return w; + + //---------- + // The ragged tail, up to 7 bytes + do{ + *w++ = *--r; + }while(r >= r0); + + return w; + } + + #endif // LOCAL + +#endif // IMPLEMENTATION diff --git a/release/x86_64/fedora41/glibc_2.40/N16.lib.c b/release/x86_64/fedora41/glibc_2.40/N16.lib.c index 3b77116..561b43d 100644 --- a/release/x86_64/fedora41/glibc_2.40/N16.lib.c +++ b/release/x86_64/fedora41/glibc_2.40/N16.lib.c @@ -111,9 +111,9 @@ N16·T* (*access)(N16·T*, Extent); void (*from_uint32)(N16·T *destination ,uint32_t value); - } N16·Λ; + } N16·M; - Local const N16·Λ N16·λ; // initialized in the LOCAL section + Local const N16·M N16·m; // initialized in the LOCAL section #endif @@ -397,7 +397,7 @@ return N16·shift_right(shift_count, spill, operand, fill); } - Local const N16·Λ N16·λ = { + Local const N16·M N16·m = { .allocate_array = N16·allocate_array ,.allocate_array_zero = N16·allocate_array_zero diff --git a/release/x86_64/fedora41/glibc_2.40/N32.lib.c b/release/x86_64/fedora41/glibc_2.40/N32.lib.c index fba3c21..ccb3411 100644 --- a/release/x86_64/fedora41/glibc_2.40/N32.lib.c +++ b/release/x86_64/fedora41/glibc_2.40/N32.lib.c @@ -112,9 +112,9 @@ N32·T* (*access)(N32·T*, Extent); void (*from_uint32)(N32·T *destination ,uint32_t value); - } N32·Λ; + } N32·M; - Local const N32·Λ N32·λ; // initialized in the LOCAL section + Local const N32·M N32·m; // initialized in the LOCAL section #endif @@ -421,7 +421,7 @@ return N32·shift_right(shift_count, spill, operand, fill); } - Local const N32·Λ N32·λ = { + Local const N32·M N32·m = { .allocate_array = N32·allocate_array ,.allocate_array_zero = N32·allocate_array_zero diff --git a/release/x86_64/fedora41/glibc_2.40/N32_1x32.lib.c b/release/x86_64/fedora41/glibc_2.40/N32_1x32.lib.c index c43bc8b..de41d70 100644 --- a/release/x86_64/fedora41/glibc_2.40/N32_1x32.lib.c +++ b/release/x86_64/fedora41/glibc_2.40/N32_1x32.lib.c @@ -1,28 +1,10 @@ -/* - N32 - a processor native type - - For binary operations: a op b -> c - - See the document on the proper use of the Natural types. - - On the subject of multiple pointers indicating the same location in memory: - - When a routine has multiple results, and one or more of the result location - pointers point to the same storage, the routine will either return an error - status, or have defined behavior. - - When a routine has multiple operands, in any combination, those - pointers can point to the same location, and the routine will - function as advertised. - When an operand functions as both an input and a result, perhaps due - to a result pointer pointing to the same place as an operand - pointer, the routine will function as advertised. (Internally the - routine might make a temporary copy of the operand to accomplish - this.) +/* + M - The type for the function dictionary (manifold). + m - a manifold instance, there can be many, m0, m1 ... + T - Is the type for the tableau. */ - #define N32_1x32·DEBUG #ifndef FACE @@ -44,9 +26,9 @@ //---------------------------------------- // Instance Data (Declaration Only) - typedef uint32_t Extent; - typedef uint32_t Digit; + typedef uint64_t Address; + // tableau type, encapsulated data is unavailable to user code typedef struct N32_1x32·T N32_1x32·T; extern N32_1x32·T *N32_1x32·zero; @@ -59,17 +41,18 @@ // Return/Error Status and handlers typedef enum{ - N32_1x32·Status·ok = 0 - ,N32_1x32·Status·overflow = 1 - ,N32_1x32·Status·accumulator1_overflow = 2 - ,N32_1x32·Status·carry = 3 - ,N32_1x32·Status·borrow = 4 - ,N32_1x32·Status·undefined_divide_by_zero = 5 - ,N32_1x32·Status·undefined_modulus_zero = 6 - ,N32_1x32·Status·gt_max_shift_count = 7 - ,N32_1x32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value - ,N32_1x32·Status·one_word_product = 9 - ,N32_1x32·Status·two_word_product = 10 + N32_1x32·Status·ok, + N32_1x32·Status·overflow, + N32_1x32·Status·accumulator_overflow, + N32_1x32·Status·carry, + N32_1x32·Status·borrow, + N32_1x32·Status·undefined_divide_by_zero, + N32_1x32·Status·undefined_modulus_zero, + N32_1x32·Status·gt_max_shift_count, + N32_1x32·Status·spill_eq_operand, // not currently signaled, result will be spill value + N32_1x32·Status·one_word_product, + N32_1x32·Status·two_word_product, + N32_1x32·Status·ConversionOverflow } N32_1x32·Status; typedef enum{ @@ -78,27 +61,67 @@ ,N32_1x32·Order_gt = 1 } N32_1x32·Order; - typedef N32_1x32·T *( *N32_1x32·Allocate_MemoryFault )(Extent); + // Incomplete conversion N32_1x32·T -> PNT, N32_1x32·T leftovers + typedef struct { + size_t scale; // this is in bytes + N32_1x32·T *d; // digits, programmer must point this to a register + } N32_1x32·Leftover_N; + + // Incomplete conversion PNT -> N32_1x32·T, PNT leftovers + #define N32_1x32·LEFTOVER_PNT(PNT)\ + typedef struct {\ + size_t scale; // this is in bytes\ + PNT leftover; // Residual value in PNT format\ + } N32_1x32·Leftover_##PNT; + + #ifdef UINT8_MAX + N32_1x32·LEFTOVER_PNT(uint8_t) + #endif + #ifdef UINT16_MAX + N32_1x32·LEFTOVER_PNT(uint16_t) + #endif + #ifdef UINT32_MAX + N32_1x32·LEFTOVER_PNT(uint32_t) + #endif + #ifdef UINT64_MAX + N32_1x32·LEFTOVER_PNT(uint64_t) + #endif + #ifdef __UINT128_MAX + N32_1x32·LEFTOVER_PNT(__uint128_t) + #endif + + // when alloc runs out of memory + typedef N32_1x32·T *( *N32_1x32·Allocate_MemoryFault )(Address); //---------------------------------------- // Interface + #define N32_1x32·TO_TYPE(PNT) N32_1x32·Status (*to_##PNT)(const N32_1x32·T *, PNT *, N32_1x32·Leftover_N *) + #define N32_1x32·FROM_TYPE(PNT) N32_1x32·Status (*from_##PNT)(const PNT *, N32_1x32·T * ,N32_1x32·Leftover_##PNT *) + typedef struct{ - N32_1x32·T *(*allocate_array_zero)(Extent, N32_1x32·Allocate_MemoryFault); - N32_1x32·T *(*allocate_array)(Extent, N32_1x32·Allocate_MemoryFault); + // memory allocation + N32_1x32·T *(*allocate_array_zero)(Address, N32_1x32·Allocate_MemoryFault); + N32_1x32·T *(*allocate_array)(Address, N32_1x32·Allocate_MemoryFault); void (*deallocate)(N32_1x32·T*); + N32_1x32·T* (*access)(N32_1x32·T*, Address); + // results fits in operand type functions void (*copy)(N32_1x32·T*, N32_1x32·T*); void (*bit_and)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); void (*bit_or)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); void (*bit_complement)(N32_1x32·T*, N32_1x32·T*); void (*bit_twos_complement)(N32_1x32·T*, N32_1x32·T*); + + // tests N32_1x32·Order (*compare)(N32_1x32·T*, N32_1x32·T*); bool (*lt)(N32_1x32·T*, N32_1x32·T*); bool (*gt)(N32_1x32·T*, N32_1x32·T*); bool (*eq)(N32_1x32·T*, N32_1x32·T*); bool (*eq_zero)(N32_1x32·T*); + + // arithmetic N32_1x32·Status (*accumulate)(N32_1x32·T *accumulator1 ,N32_1x32·T *accumulator0 ,...); N32_1x32·Status (*add)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); bool (*increment)(N32_1x32·T *a); @@ -106,15 +129,39 @@ N32_1x32·Status (*multiply)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); N32_1x32·Status (*divide)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); N32_1x32·Status (*modulus)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); - N32_1x32·Status (*shift_left)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); - N32_1x32·Status (*shift_right)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); - N32_1x32·Status (*arithmetic_shift_right)(Extent, N32_1x32·T*, N32_1x32·T*); - - N32_1x32·T* (*access)(N32_1x32·T*, Extent); - void (*from_uint32)(N32_1x32·T *destination ,uint32_t value); - } N32_1x32·Λ; - Local const N32_1x32·Λ N32_1x32·λ; // initialized in the LOCAL section + // shift + N32_1x32·Status (*shift_left)(Address, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*shift_right)(Address, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*arithmetic_shift_right)(Address, N32_1x32·T*, N32_1x32·T*); + + // import/export + char *(*to_string)(N32_1x32·T *); + + #ifdef UINT8_MAX + N32_1x32·TO_TYPE(uint8_t) + N32_1x32·FROM_TYPE(uint8_t) + #endif + #ifdef UINT16_MAX + N32_1x32·TO_TYPE(uint16_t) + N32_1x32·FROM_TYPE(uint16_t) + #endif + #ifdef UINT32_MAX + N32_1x32·TO_TYPE(uint32_t) + N32_1x32·FROM_TYPE(uint32_t) + #endif + #ifdef UINT64_MAX + N32_1x32·TO_TYPE(uint64_t) + N32_1x32·FROM_TYPE(uint64_t) + #endif + #ifdef __UINT128_MAX + N32_1x32·TO_TYPE(__uint128_t) + N32_1x32·FROM_TYPE(__uint128_t) + #endif + + } N32_1x32·M; + + Local const N32_1x32·M N32_1x32·m; // initialized in the LOCAL section #endif @@ -123,31 +170,23 @@ #ifdef N32_1x32·IMPLEMENTATION - // this part goes into the library + typedef uint32_t Digit; + const uint8_t digit_array_extent = {3}; + + // full type definition for Tableau + struct N32_1x32·T{ + Digit d[digit_array_extent + 1]; + }; + + // this part goes into Nlib.a #ifndef LOCAL #include #include - - struct N32_1x32·T{ - Digit d0; - }; - - N32_1x32·T N32_1x32·constant[4] = { - {.d0 = 0}, - {.d0 = 1}, - {.d0 = ~(uint32_t)0}, - {.d0 = 1 << 31} - }; - - N32_1x32·T *N32_1x32·zero = &N32_1x32·constant[0]; - N32_1x32·T *N32_1x32·one = &N32_1x32·constant[1]; - N32_1x32·T *N32_1x32·all_one_bit = &N32_1x32·constant[2]; - N32_1x32·T *N32_1x32·msb = &N32_1x32·constant[3]; - N32_1x32·T *N32_1x32·lsb = &N32_1x32·constant[1]; + #include // the allocate an array of N32 - N32_1x32·T *N32_1x32·allocate_array(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *N32_1x32·allocate_array(Address extent ,N32_1x32·Allocate_MemoryFault memory_fault){ N32_1x32·T *instance = malloc((extent + 1) * sizeof(N32_1x32·T) ); if(!instance){ return memory_fault ? memory_fault(extent) : NULL; @@ -155,7 +194,7 @@ return instance; } - N32_1x32·T *N32_1x32·allocate_array_zero(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *N32_1x32·allocate_array_zero(Address extent ,N32_1x32·Allocate_MemoryFault memory_fault){ N32_1x32·T *instance = calloc( extent + 1 ,sizeof(N32_1x32·T) ); if(!instance){ return memory_fault ? memory_fault(extent) : NULL; @@ -167,16 +206,42 @@ free(unencumbered); } + char *to_string(N32_1x32·T *n) { + // Each byte requires two hex characters, plus "0x" prefix and null terminator + const Address string_length = (sizeof(Digit) * (digit_array_extent + 1) * 2) + 3; + char *buffer = malloc(string_length); + if (!buffer) { + return NULL; // Handle allocation failure + } + + strcpy(buffer, "0x"); // Prefix the hex representation + char *ps = buffer + 2; // Pointer to string buffer (after "0x") + + // Pointer to the most significant digit + Digit *pd = n->d + digit_array_extent; + + for (; pd >= n->d; pd--) { + sprintf(ps, "%0*X", (int)(sizeof(Digit) * 2), *pd); + ps += sizeof(Digit) * 2; // Move forward in buffer + } + + return buffer; // Caller must free the allocated buffer + } + #endif - // This part is included after the library user's code + // This part is included after the user's code. If the code at top is a 'header, then this is a 'tailer'. #ifdef LOCAL - // instance + #include "Copy.lib.c" - struct N32_1x32·T{ - Digit d0; - }; + CON32_1x32TANTS_BLOCK + + N32_1x32·T *N32_1x32·zero = N32_1x32·constant + 0; + N32_1x32·T *N32_1x32·one = N32_1x32·constant + 1; + N32_1x32·T *N32_1x32·all_one_bit = N32_1x32·constant + 2; + N32_1x32·T *N32_1x32·msb = &N32_1x32·constant + 3; + N32_1x32·T *N32_1x32·lsb = &N32_1x32·constant + 1; // temporary variables // making these LOCAL rather than reserving one block in the library is thread safe @@ -188,21 +253,16 @@ // allocation - extern N32_1x32·T *N32_1x32·allocate_array(Extent, N32_1x32·Allocate_MemoryFault); - extern N32_1x32·T *N32_1x32·allocate_array_zero(Extent, N32_1x32·Allocate_MemoryFault); + extern N32_1x32·T *N32_1x32·allocate_array(Address, N32_1x32·Allocate_MemoryFault); + extern N32_1x32·T *N32_1x32·allocate_array_zero(Address, N32_1x32·Allocate_MemoryFault); extern void N32_1x32·deallocate(N32_1x32·T *); // so the user can access numbers in an array allocation - Local N32_1x32·T* N32_1x32·access(N32_1x32·T *array ,Extent index){ - return &array[index]; - } - - Local void N32_1x32·from_uint32(N32_1x32·T *destination ,uint32_t value){ - if(destination == NULL) return; - destination->d0 = value; + Local N32_1x32·T* N32_1x32·access(N32_1x32·T *array ,Address index){ + return array + index; } - // copy, convenience copy + // copy Local void N32_1x32·copy(N32_1x32·T *destination ,N32_1x32·T *source){ if(source == destination) return; // that was easy! @@ -311,7 +371,6 @@ return (diff > a->d0) ? N32_1x32·Status·borrow : N32_1x32·Status·ok; } - Local N32_1x32·Status N32_1x32·multiply(N32_1x32·T *product1 ,N32_1x32·T *product0 ,N32_1x32·T *a ,N32_1x32·T *b){ uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0; product0->d0 = (uint32_t)product; @@ -421,7 +480,17 @@ return N32_1x32·shift_right(shift_count, spill, operand, fill); } - Local const N32_1x32·Λ N32_1x32·λ = { + #ifdef UINT8_MAX + #endif + #ifdef UINT16_MAX + #endif + #ifdef UINT32_MAX + #endif + #ifdef UINT64_MAX + #endif + + // Tableau share dictionary + Local const N32_1x32·M N32_1x32·m = { .allocate_array = N32_1x32·allocate_array ,.allocate_array_zero = N32_1x32·allocate_array_zero @@ -450,8 +519,23 @@ ,.access = N32_1x32·access ,.from_uint32 = N32_1x32·from_uint32 + + #ifdef UINT8_MAX + #endif + #ifdef UINT16_MAX + #endif + #ifdef UINT32_MAX + #endif + #ifdef UINT64_MAX + #endif + #ifdef __UINT128_MAX + #endif + }; + #undef FACE + #include "Copy.lib.c" + #endif #endif diff --git a/release/x86_64/fedora41/glibc_2.40/N32_4x8.lib.c b/release/x86_64/fedora41/glibc_2.40/N32_4x8.lib.c index 22cced5..b66c143 100644 --- a/release/x86_64/fedora41/glibc_2.40/N32_4x8.lib.c +++ b/release/x86_64/fedora41/glibc_2.40/N32_4x8.lib.c @@ -112,9 +112,9 @@ N32_4x8·T* (*access)(N32_4x8·T*, Extent); void (*from_uint32)(N32_4x8·T *destination ,uint32_t value); - } N32_4x8·Λ; + } N32_4x8·M; - Local const N32_4x8·Λ N32_4x8·λ; // initialized in the LOCAL section + Local const N32_4x8·M N32_4x8·m; // initialized in the LOCAL section #endif @@ -421,7 +421,7 @@ return N32_4x8·shift_right(shift_count, spill, operand, fill); } - Local const N32_4x8·Λ N32_4x8·λ = { + Local const N32_4x8·M N32_4x8·m = { .allocate_array = N32_4x8·allocate_array ,.allocate_array_zero = N32_4x8·allocate_array_zero diff --git a/release/x86_64/fedora41/glibc_2.40/N64.lib.c b/release/x86_64/fedora41/glibc_2.40/N64.lib.c index 9d98d58..6058e9b 100644 --- a/release/x86_64/fedora41/glibc_2.40/N64.lib.c +++ b/release/x86_64/fedora41/glibc_2.40/N64.lib.c @@ -114,9 +114,9 @@ N64·T* (*access)(N64·T*, Extent); void (*from_uint64)(N64·T *destination, uint64_t value); - } N64·Λ; + } N64·M; - Local const N64·Λ N64·λ; // initialized in the LOCAL section + Local const N64·M N64·m; // initialized in the LOCAL section #endif @@ -442,7 +442,7 @@ return N64·shift_right(shift_count, spill, operand, fill); } - Local const N64·Λ N64·λ = { + Local const N64·M N64·m = { .allocate_array = N64·allocate_array ,.allocate_array_zero = N64·allocate_array_zero ,.deallocate = N64·deallocate diff --git a/release/x86_64/fedora41/glibc_2.40/N8.lib.c b/release/x86_64/fedora41/glibc_2.40/N8.lib.c index ad52309..df6e01c 100644 --- a/release/x86_64/fedora41/glibc_2.40/N8.lib.c +++ b/release/x86_64/fedora41/glibc_2.40/N8.lib.c @@ -111,9 +111,9 @@ N8·T* (*access)(N8·T*, Extent); void (*from_uint32)(N8·T *destination ,uint32_t value); - } N8·Λ; + } N8·M; - Local const N8·Λ N8·λ; // initialized in the LOCAL section + Local const N8·M N8·m; // initialized in the LOCAL section #endif @@ -397,7 +397,7 @@ return N8·shift_right(shift_count, spill, operand, fill); } - Local const N8·Λ N8·λ = { + Local const N8·M N8·m = { .allocate_array = N8·allocate_array ,.allocate_array_zero = N8·allocate_array_zero diff --git a/release/x86_64/fedora41/glibc_2.40/libN.a b/release/x86_64/fedora41/glibc_2.40/libN.a index f81996f2e65a8979c0c5b7c69d74052be59f1f32..522f3dfe8c40b40064e1b6fca1ff5c745657266f 100644 GIT binary patch delta 75 zcmdni!MFj4q&S&!873<*Moivd$FaGVQVuTZ7q(Of^6`uFVJK btib&9Or>CcwPrAgzxljz2bhkqyTJ+otMMYL diff --git a/tester/cc/Copy.lib.c b/tester/cc/Copy.lib.c new file mode 100644 index 0000000..2563ec0 --- /dev/null +++ b/tester/cc/Copy.lib.c @@ -0,0 +1,152 @@ +/* + Copy - Memory copy operations with attention to alignment. + Provides optimized copy and byte order reversal functions. +*/ + +#define Copy·DEBUG + +#ifndef FACE +#define Copy·IMPLEMENTATION +#define FACE +#endif + +//-------------------------------------------------------------------------------- +// Interface + +#ifndef Copy·FACE +#define Copy·FACE + + #include + #include + + void *Copy·region(void *read0 ,void *read1 ,void *write0); + void *Copy·reverse_byte_order(void *read0 ,void *read1 ,void *write0); + +#endif + +//-------------------------------------------------------------------------------- +// Implementation + +#ifdef Copy·IMPLEMENTATION + + // this part goes into Nlib.a + #ifndef LOCAL + #endif + + #ifdef LOCAL + + /* + Copy·region - Copies a memory region while preserving byte order. + - Aligns reads for performance. + - Writes are assumed to be buffered and do not require alignment. + - Returns the updated write pointer. + */ + void *Copy·region(void *read0 ,void *read1 ,void *write0){ + + uint8_t *r = (uint8_t *)read0; + uint8_t *r1 = (uint8_t *)read1; + uint8_t *w = (uint8_t *)write0; + + //---------- + // The potentially unaligned initial part (align read pointer). + if( (uintptr_t)r & 0x7 ){ + + // ORing in `0x7` adds at most six bytes to r. + uint8_t *r01 = (uint8_t *)((uintptr_t)r | 0x7); + + // If the read interval is very small + if(r01 >= r1){ + while(r < r1){ + *w++ = *r++; + } + return w; + } + + // Copy up to alignment boundary + do{ + *w++ = *r++; + }while(r <= r01); + } + // r is now aligned, but *r has not yet been copied + + //---------- + // The bulk copy part (w is still possibly unaligned, but r is aligned) + uint8_t *r10 = (uint8_t *)((uintptr_t)r1 & ~(uintptr_t)0x7); + + while(r < r10){ + *(uint64_t *)w = *(uint64_t *)r; + w += 8; + r += 8; + } + + // If r1 was aligned then r10 == r1 and we are done + if(r == r1) return w; + + //---------- + // The ragged tail, up to 7 bytes + do{ + *w++ = *r++; + }while(r < r1); + + return w; + } + + /* + Copy·reverse_byte_order - Copies a memory region while reversing byte order. + - Reads in reverse order while writing in forward order. + - Uses `__builtin_bswap64` for efficient 64-bit swaps. + - Returns the updated write pointer. + */ + void *Copy·reverse_byte_order(void *read0 ,void *read1 ,void *write0){ + + uint8_t *r = (uint8_t *)read1; // Start from the last byte + uint8_t *r0 = (uint8_t *)read0; + uint8_t *w = (uint8_t *)write0; + + //---------- + // The potentially unaligned initial part (align read pointer). + if( (uintptr_t)r & 0x7 ){ + + // ANDing with `~0x7` moves it downward to the nearest lower alignment. + uint8_t *r10 = (uint8_t *)((uintptr_t)r & ~(uintptr_t)0x7); + + // If the read interval is very small + if(r10 < r0){ + while(r > r0){ + *w++ = *--r; + } + return w; + } + + // Copy down to alignment boundary + do{ + *w++ = *--r; + }while(r > r10); + } + // r is now aligned, and *r has been copied + + //---------- + // The bulk copy part + uint8_t *r01 = (uint8_t *)( ((uintptr_t)r0 + (uintptr_t)0x7) & ~(uintptr_t)0x7); + + while(r > r01){ + r -= 8; + *(uint64_t *)w = __builtin_bswap64(*(uint64_t *)r); + w += 8; + } + + // If r0 was aligned then r01 == r0 and we are done + if(r < r0) return w; + + //---------- + // The ragged tail, up to 7 bytes + do{ + *w++ = *--r; + }while(r >= r0); + + return w; + } + + #endif // LOCAL + +#endif // IMPLEMENTATION diff --git a/tester/cc/N16PN.lib.c b/tester/cc/N16PN.lib.c index bd63d9e..d7a2e05 100644 --- a/tester/cc/N16PN.lib.c +++ b/tester/cc/N16PN.lib.c @@ -111,9 +111,9 @@ N16PN·T* (*access)(N16PN·T*, Extent); void (*from_uint32)(N16PN·T *destination ,uint32_t value); - } N16PN·M; + } N16PN·Λ; - Local const N16PN·M N16PN·m; // initialized in the LOCAL section + Local const N16PN·Λ N16PN·λ; // initialized in the LOCAL section #endif @@ -397,7 +397,7 @@ return N16PN·shift_right(shift_count, spill, operand, fill); } - Local const N16PN·M N16PN·m = { + Local const N16PN·Λ N16PN·λ = { .allocate_array = N16PN·allocate_array ,.allocate_array_zero = N16PN·allocate_array_zero diff --git a/tester/cc/N32PN.lib.c b/tester/cc/N32PN.lib.c index b62a6c4..952f570 100644 --- a/tester/cc/N32PN.lib.c +++ b/tester/cc/N32PN.lib.c @@ -112,9 +112,9 @@ N32PN·T* (*access)(N32PN·T*, Extent); void (*from_uint32)(N32PN·T *destination ,uint32_t value); - } N32PN·M; + } N32PN·Λ; - Local const N32PN·M N32PN·m; // initialized in the LOCAL section + Local const N32PN·Λ N32PN·λ; // initialized in the LOCAL section #endif @@ -421,7 +421,7 @@ return N32PN·shift_right(shift_count, spill, operand, fill); } - Local const N32PN·M N32PN·m = { + Local const N32PN·Λ N32PN·λ = { .allocate_array = N32PN·allocate_array ,.allocate_array_zero = N32PN·allocate_array_zero diff --git a/tester/cc/N32_1x32.lib.c b/tester/cc/N32_1x32.lib.c index c2f97f2..de41d70 100644 --- a/tester/cc/N32_1x32.lib.c +++ b/tester/cc/N32_1x32.lib.c @@ -1,28 +1,10 @@ -/* - N32 - a processor native type - - For binary operations: a op b -> c - - See the document on the proper use of the Natural types. - - On the subject of multiple pointers indicating the same location in memory: - - When a routine has multiple results, and one or more of the result location - pointers point to the same storage, the routine will either return an error - status, or have defined behavior. - - When a routine has multiple operands, in any combination, those - pointers can point to the same location, and the routine will - function as advertised. - When an operand functions as both an input and a result, perhaps due - to a result pointer pointing to the same place as an operand - pointer, the routine will function as advertised. (Internally the - routine might make a temporary copy of the operand to accomplish - this.) +/* + M - The type for the function dictionary (manifold). + m - a manifold instance, there can be many, m0, m1 ... + T - Is the type for the tableau. */ - #define N32_1x32·DEBUG #ifndef FACE @@ -44,9 +26,9 @@ //---------------------------------------- // Instance Data (Declaration Only) - typedef uint32_t Extent; - typedef uint32_t Digit; + typedef uint64_t Address; + // tableau type, encapsulated data is unavailable to user code typedef struct N32_1x32·T N32_1x32·T; extern N32_1x32·T *N32_1x32·zero; @@ -59,17 +41,18 @@ // Return/Error Status and handlers typedef enum{ - N32_1x32·Status·ok = 0 - ,N32_1x32·Status·overflow = 1 - ,N32_1x32·Status·accumulator1_overflow = 2 - ,N32_1x32·Status·carry = 3 - ,N32_1x32·Status·borrow = 4 - ,N32_1x32·Status·undefined_divide_by_zero = 5 - ,N32_1x32·Status·undefined_modulus_zero = 6 - ,N32_1x32·Status·gt_max_shift_count = 7 - ,N32_1x32·Status·spill_eq_operand = 8 // not currently signaled, result will be spill value - ,N32_1x32·Status·one_word_product = 9 - ,N32_1x32·Status·two_word_product = 10 + N32_1x32·Status·ok, + N32_1x32·Status·overflow, + N32_1x32·Status·accumulator_overflow, + N32_1x32·Status·carry, + N32_1x32·Status·borrow, + N32_1x32·Status·undefined_divide_by_zero, + N32_1x32·Status·undefined_modulus_zero, + N32_1x32·Status·gt_max_shift_count, + N32_1x32·Status·spill_eq_operand, // not currently signaled, result will be spill value + N32_1x32·Status·one_word_product, + N32_1x32·Status·two_word_product, + N32_1x32·Status·ConversionOverflow } N32_1x32·Status; typedef enum{ @@ -78,27 +61,67 @@ ,N32_1x32·Order_gt = 1 } N32_1x32·Order; - typedef N32_1x32·T *( *N32_1x32·Allocate_MemoryFault )(Extent); + // Incomplete conversion N32_1x32·T -> PNT, N32_1x32·T leftovers + typedef struct { + size_t scale; // this is in bytes + N32_1x32·T *d; // digits, programmer must point this to a register + } N32_1x32·Leftover_N; + + // Incomplete conversion PNT -> N32_1x32·T, PNT leftovers + #define N32_1x32·LEFTOVER_PNT(PNT)\ + typedef struct {\ + size_t scale; // this is in bytes\ + PNT leftover; // Residual value in PNT format\ + } N32_1x32·Leftover_##PNT; + + #ifdef UINT8_MAX + N32_1x32·LEFTOVER_PNT(uint8_t) + #endif + #ifdef UINT16_MAX + N32_1x32·LEFTOVER_PNT(uint16_t) + #endif + #ifdef UINT32_MAX + N32_1x32·LEFTOVER_PNT(uint32_t) + #endif + #ifdef UINT64_MAX + N32_1x32·LEFTOVER_PNT(uint64_t) + #endif + #ifdef __UINT128_MAX + N32_1x32·LEFTOVER_PNT(__uint128_t) + #endif + + // when alloc runs out of memory + typedef N32_1x32·T *( *N32_1x32·Allocate_MemoryFault )(Address); //---------------------------------------- // Interface + #define N32_1x32·TO_TYPE(PNT) N32_1x32·Status (*to_##PNT)(const N32_1x32·T *, PNT *, N32_1x32·Leftover_N *) + #define N32_1x32·FROM_TYPE(PNT) N32_1x32·Status (*from_##PNT)(const PNT *, N32_1x32·T * ,N32_1x32·Leftover_##PNT *) + typedef struct{ - N32_1x32·T *(*allocate_array_zero)(Extent, N32_1x32·Allocate_MemoryFault); - N32_1x32·T *(*allocate_array)(Extent, N32_1x32·Allocate_MemoryFault); + // memory allocation + N32_1x32·T *(*allocate_array_zero)(Address, N32_1x32·Allocate_MemoryFault); + N32_1x32·T *(*allocate_array)(Address, N32_1x32·Allocate_MemoryFault); void (*deallocate)(N32_1x32·T*); + N32_1x32·T* (*access)(N32_1x32·T*, Address); + // results fits in operand type functions void (*copy)(N32_1x32·T*, N32_1x32·T*); void (*bit_and)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); void (*bit_or)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); void (*bit_complement)(N32_1x32·T*, N32_1x32·T*); void (*bit_twos_complement)(N32_1x32·T*, N32_1x32·T*); + + // tests N32_1x32·Order (*compare)(N32_1x32·T*, N32_1x32·T*); bool (*lt)(N32_1x32·T*, N32_1x32·T*); bool (*gt)(N32_1x32·T*, N32_1x32·T*); bool (*eq)(N32_1x32·T*, N32_1x32·T*); bool (*eq_zero)(N32_1x32·T*); + + // arithmetic N32_1x32·Status (*accumulate)(N32_1x32·T *accumulator1 ,N32_1x32·T *accumulator0 ,...); N32_1x32·Status (*add)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); bool (*increment)(N32_1x32·T *a); @@ -106,12 +129,36 @@ N32_1x32·Status (*multiply)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); N32_1x32·Status (*divide)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); N32_1x32·Status (*modulus)(N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); - N32_1x32·Status (*shift_left)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); - N32_1x32·Status (*shift_right)(Extent, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); - N32_1x32·Status (*arithmetic_shift_right)(Extent, N32_1x32·T*, N32_1x32·T*); - N32_1x32·T* (*access)(N32_1x32·T*, Extent); - void (*from_uint32)(N32_1x32·T *destination ,uint32_t value); + // shift + N32_1x32·Status (*shift_left)(Address, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*shift_right)(Address, N32_1x32·T*, N32_1x32·T*, N32_1x32·T*); + N32_1x32·Status (*arithmetic_shift_right)(Address, N32_1x32·T*, N32_1x32·T*); + + // import/export + char *(*to_string)(N32_1x32·T *); + + #ifdef UINT8_MAX + N32_1x32·TO_TYPE(uint8_t) + N32_1x32·FROM_TYPE(uint8_t) + #endif + #ifdef UINT16_MAX + N32_1x32·TO_TYPE(uint16_t) + N32_1x32·FROM_TYPE(uint16_t) + #endif + #ifdef UINT32_MAX + N32_1x32·TO_TYPE(uint32_t) + N32_1x32·FROM_TYPE(uint32_t) + #endif + #ifdef UINT64_MAX + N32_1x32·TO_TYPE(uint64_t) + N32_1x32·FROM_TYPE(uint64_t) + #endif + #ifdef __UINT128_MAX + N32_1x32·TO_TYPE(__uint128_t) + N32_1x32·FROM_TYPE(__uint128_t) + #endif + } N32_1x32·M; Local const N32_1x32·M N32_1x32·m; // initialized in the LOCAL section @@ -123,31 +170,23 @@ #ifdef N32_1x32·IMPLEMENTATION - // this part goes into the library + typedef uint32_t Digit; + const uint8_t digit_array_extent = {3}; + + // full type definition for Tableau + struct N32_1x32·T{ + Digit d[digit_array_extent + 1]; + }; + + // this part goes into Nlib.a #ifndef LOCAL #include #include - - struct N32_1x32·T{ - Digit d0; - }; - - N32_1x32·T N32_1x32·constant[4] = { - {.d0 = 0}, - {.d0 = 1}, - {.d0 = ~(uint32_t)0}, - {.d0 = 1 << 31} - }; - - N32_1x32·T *N32_1x32·zero = &N32_1x32·constant[0]; - N32_1x32·T *N32_1x32·one = &N32_1x32·constant[1]; - N32_1x32·T *N32_1x32·all_one_bit = &N32_1x32·constant[2]; - N32_1x32·T *N32_1x32·msb = &N32_1x32·constant[3]; - N32_1x32·T *N32_1x32·lsb = &N32_1x32·constant[1]; + #include // the allocate an array of N32 - N32_1x32·T *N32_1x32·allocate_array(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *N32_1x32·allocate_array(Address extent ,N32_1x32·Allocate_MemoryFault memory_fault){ N32_1x32·T *instance = malloc((extent + 1) * sizeof(N32_1x32·T) ); if(!instance){ return memory_fault ? memory_fault(extent) : NULL; @@ -155,7 +194,7 @@ return instance; } - N32_1x32·T *N32_1x32·allocate_array_zero(Extent extent ,N32_1x32·Allocate_MemoryFault memory_fault){ + N32_1x32·T *N32_1x32·allocate_array_zero(Address extent ,N32_1x32·Allocate_MemoryFault memory_fault){ N32_1x32·T *instance = calloc( extent + 1 ,sizeof(N32_1x32·T) ); if(!instance){ return memory_fault ? memory_fault(extent) : NULL; @@ -167,16 +206,42 @@ free(unencumbered); } + char *to_string(N32_1x32·T *n) { + // Each byte requires two hex characters, plus "0x" prefix and null terminator + const Address string_length = (sizeof(Digit) * (digit_array_extent + 1) * 2) + 3; + char *buffer = malloc(string_length); + if (!buffer) { + return NULL; // Handle allocation failure + } + + strcpy(buffer, "0x"); // Prefix the hex representation + char *ps = buffer + 2; // Pointer to string buffer (after "0x") + + // Pointer to the most significant digit + Digit *pd = n->d + digit_array_extent; + + for (; pd >= n->d; pd--) { + sprintf(ps, "%0*X", (int)(sizeof(Digit) * 2), *pd); + ps += sizeof(Digit) * 2; // Move forward in buffer + } + + return buffer; // Caller must free the allocated buffer + } + #endif - // This part is included after the library user's code + // This part is included after the user's code. If the code at top is a 'header, then this is a 'tailer'. #ifdef LOCAL - // instance + #include "Copy.lib.c" - struct N32_1x32·T{ - Digit d0; - }; + CON32_1x32TANTS_BLOCK + + N32_1x32·T *N32_1x32·zero = N32_1x32·constant + 0; + N32_1x32·T *N32_1x32·one = N32_1x32·constant + 1; + N32_1x32·T *N32_1x32·all_one_bit = N32_1x32·constant + 2; + N32_1x32·T *N32_1x32·msb = &N32_1x32·constant + 3; + N32_1x32·T *N32_1x32·lsb = &N32_1x32·constant + 1; // temporary variables // making these LOCAL rather than reserving one block in the library is thread safe @@ -188,21 +253,16 @@ // allocation - extern N32_1x32·T *N32_1x32·allocate_array(Extent, N32_1x32·Allocate_MemoryFault); - extern N32_1x32·T *N32_1x32·allocate_array_zero(Extent, N32_1x32·Allocate_MemoryFault); + extern N32_1x32·T *N32_1x32·allocate_array(Address, N32_1x32·Allocate_MemoryFault); + extern N32_1x32·T *N32_1x32·allocate_array_zero(Address, N32_1x32·Allocate_MemoryFault); extern void N32_1x32·deallocate(N32_1x32·T *); // so the user can access numbers in an array allocation - Local N32_1x32·T* N32_1x32·access(N32_1x32·T *array ,Extent index){ - return &array[index]; - } - - Local void N32_1x32·from_uint32(N32_1x32·T *destination ,uint32_t value){ - if(destination == NULL) return; - destination->d0 = value; + Local N32_1x32·T* N32_1x32·access(N32_1x32·T *array ,Address index){ + return array + index; } - // copy, convenience copy + // copy Local void N32_1x32·copy(N32_1x32·T *destination ,N32_1x32·T *source){ if(source == destination) return; // that was easy! @@ -311,7 +371,6 @@ return (diff > a->d0) ? N32_1x32·Status·borrow : N32_1x32·Status·ok; } - Local N32_1x32·Status N32_1x32·multiply(N32_1x32·T *product1 ,N32_1x32·T *product0 ,N32_1x32·T *a ,N32_1x32·T *b){ uint64_t product = (uint64_t)a->d0 * (uint64_t)b->d0; product0->d0 = (uint32_t)product; @@ -421,6 +480,16 @@ return N32_1x32·shift_right(shift_count, spill, operand, fill); } + #ifdef UINT8_MAX + #endif + #ifdef UINT16_MAX + #endif + #ifdef UINT32_MAX + #endif + #ifdef UINT64_MAX + #endif + + // Tableau share dictionary Local const N32_1x32·M N32_1x32·m = { .allocate_array = N32_1x32·allocate_array @@ -450,8 +519,23 @@ ,.access = N32_1x32·access ,.from_uint32 = N32_1x32·from_uint32 + + #ifdef UINT8_MAX + #endif + #ifdef UINT16_MAX + #endif + #ifdef UINT32_MAX + #endif + #ifdef UINT64_MAX + #endif + #ifdef __UINT128_MAX + #endif + }; + #undef FACE + #include "Copy.lib.c" + #endif #endif diff --git a/tester/cc/N64PN.lib.c b/tester/cc/N64PN.lib.c index 97f74fc..583ad08 100644 --- a/tester/cc/N64PN.lib.c +++ b/tester/cc/N64PN.lib.c @@ -114,9 +114,9 @@ N64PN·T* (*access)(N64PN·T*, Extent); void (*from_uint64)(N64PN·T *destination, uint64_t value); - } N64PN·M; + } N64PN·Λ; - Local const N64PN·M N64PN·m; // initialized in the LOCAL section + Local const N64PN·Λ N64PN·λ; // initialized in the LOCAL section #endif @@ -442,7 +442,7 @@ return N64PN·shift_right(shift_count, spill, operand, fill); } - Local const N64PN·M N64PN·m = { + Local const N64PN·Λ N64PN·λ = { .allocate_array = N64PN·allocate_array ,.allocate_array_zero = N64PN·allocate_array_zero ,.deallocate = N64PN·deallocate diff --git a/tester/cc/N8PN.lib.c b/tester/cc/N8PN.lib.c index 3955f1c..9cfd66d 100644 --- a/tester/cc/N8PN.lib.c +++ b/tester/cc/N8PN.lib.c @@ -111,9 +111,9 @@ N8PN·T* (*access)(N8PN·T*, Extent); void (*from_uint32)(N8PN·T *destination ,uint32_t value); - } N8PN·M; + } N8PN·Λ; - Local const N8PN·M N8PN·m; // initialized in the LOCAL section + Local const N8PN·Λ N8PN·λ; // initialized in the LOCAL section #endif @@ -397,7 +397,7 @@ return N8PN·shift_right(shift_count, spill, operand, fill); } - Local const N8PN·M N8PN·m = { + Local const N8PN·Λ N8PN·λ = { .allocate_array = N8PN·allocate_array ,.allocate_array_zero = N8PN·allocate_array_zero diff --git "a/tester/cc\360\237\226\211/test_Copy_0.cli.c" "b/tester/cc\360\237\226\211/test_Copy_0.cli.c" new file mode 100644 index 0000000..7609728 --- /dev/null +++ "b/tester/cc\360\237\226\211/test_Copy_0.cli.c" @@ -0,0 +1,269 @@ +/* + Black Box test for Copy.lib.c + +*/ + +#include +#include +#include +#include +#include + +// Pull in the interface portion of the Copy library +#define FACE +#include "Copy.lib.c" +#undef FACE + +static sigjmp_buf jump_buffer; + +void signal_handler( int sig ){ + siglongjmp( jump_buffer ,1 ); +} + +typedef bool ( *TestFunction )(); +typedef struct{ + TestFunction function; + const char *name; +} TestEntry; + +// ----------------------------------------------------------------------------- +// Test Declarations +// ----------------------------------------------------------------------------- + +bool test_copy_bytes(); +bool test_copy_bytes_reverse(); +bool test_copy_step(); +bool test_copy_step_reverse(); + +// ----------------------------------------------------------------------------- +// Test List and Runner +// ----------------------------------------------------------------------------- + +TestEntry test_list[] = { + { test_copy_bytes ,"test_copy_bytes" } + ,{ test_copy_bytes_reverse ,"test_copy_bytes_reverse" } + ,{ test_copy_step ,"test_copy_step" } + ,{ test_copy_step_reverse ,"test_copy_step_reverse" } + ,{ NULL ,NULL } // terminator +}; + +int test_head(){ + int pass_count = 0; + int fail_count = 0; + + // Catch common signals + signal( SIGSEGV ,signal_handler ); + signal( SIGFPE ,signal_handler ); + signal( SIGABRT ,signal_handler ); + + for( TestEntry *entry = test_list ; entry->function != NULL ; entry++ ){ + if( sigsetjmp( jump_buffer ,1 ) == 0 ){ + // Normal path + if( !entry->function() ){ + printf( "Failed: %s\n" ,entry->name ); + fail_count++; + }else{ + pass_count++; + } + }else{ + // Signal caught + printf( "Failed due to signal: %s\n" ,entry->name ); + fail_count++; + } + } + + printf( "Tests passed: %d\n" ,pass_count ); + printf( "Tests failed: %d\n" ,fail_count ); + + return ( fail_count == 0 ) ? 0 : 1; +} + +int main( int argc ,char **argv ){ + return test_head(); +} + +// ----------------------------------------------------------------------------- +// Test Definitions +// ----------------------------------------------------------------------------- + +bool test_copy_bytes(){ + // Test that Copy·bytes() copies data verbatim from src to dst + uint8_t src[8] = { 0x01 ,0x02 ,0x03 ,0x04 ,0xA0 ,0xB0 ,0xC0 ,0xFF }; + uint8_t dst[8]; + memset( dst ,0x00 ,8 ); + + // Copy entire buffer + Copy·bytes( src ,src + 8 ,dst ); + + // Check + for( int i = 0 ; i < 8 ; i++ ){ + if( dst[i] != src[i] ){ + return false; + } + } + return true; +} + +bool test_copy_bytes_reverse(){ + // Test that Copy·bytes_reverse_order() reverses the entire buffer + uint8_t src[6] = { 'H' ,'e' ,'l' ,'l' ,'o' ,'!' }; + uint8_t dst[6]; + memset( dst ,0x00 ,6 ); + + // Reverse copy: Expect "!olleH" + Copy·bytes_reverse_order( src ,src + 6 ,dst ); + + static const uint8_t expected[6] = { '!' ,'o' ,'l' ,'l' ,'e' ,'H' }; + for( int i = 0 ; i < 6 ; i++ ){ + if( dst[i] != expected[i] ){ + return false; + } + } + return true; +} + +bool test_copy_step(){ + // Test partial copying with Copy·step() + // We'll create a source of 10 bytes but allow only 4 bytes of write at a time. + uint8_t src[10]; + for( int i = 0 ; i < 10 ; i++ ){ + src[i] = (uint8_t)( i + 1 ); // 1..10 + } + + uint8_t dst[10]; + memset( dst ,0 ,10 ); + + // Create a Copy·it descriptor + Copy·it it; + it.read0 = src; + it.read_size = 10; + it.write0 = dst; + it.write_size = 4; + + // First step: expect incomplete_read (only 4 bytes written) + { + Copy·Status st = Copy·step( &it ); + if( st != Copy·Status·incomplete_read ){ + return false; + } + // Check that first 4 bytes got copied + for( int i = 0 ; i < 4 ; i++ ){ + if( dst[i] != (uint8_t)( i + 1 ) ){ + return false; + } + } + // read_size=6, write_size=0, read0 advanced by 4, write0 advanced by 4 + if( it.read_size != 6 || it.write_size != 0 ){ + return false; + } + } + + // Provide more write space + it.write_size = 3; + // Second step: expect incomplete_read again (only 3 more bytes copied) + { + Copy·Status st = Copy·step( &it ); + if( st != Copy·Status·incomplete_read ){ + return false; + } + // Check next 3 bytes: now in dst[4..6] + for( int i = 0 ; i < 3 ; i++ ){ + // i=0 => index=4 => src[4]=5 + if( dst[4 + i] != (uint8_t)( 5 + i ) ){ + return false; + } + } + if( it.read_size != 3 || it.write_size != 0 ){ + return false; + } + } + + // Provide final 3 bytes of write space + it.write_size = 3; + { + Copy·Status st = Copy·step( &it ); + // Now we expect either complete or incomplete_write if exactly matched + // Actually we have 3 left to read, 3 left to write => it should be 'complete' + if( st != Copy·Status·complete ){ + return false; + } + // Check last 3 bytes + for( int i = 0 ; i < 3 ; i++ ){ + if( dst[7 + i] != (uint8_t)( 8 + i ) ){ + return false; + } + } + } + + // Verify the entire dst array + for( int i = 0 ; i < 10 ; i++ ){ + if( dst[i] != (uint8_t)( i + 1 ) ){ + return false; + } + } + return true; +} + +bool test_copy_step_reverse(){ + // Test partial copying in reverse using Copy·step_reverse_order() + + // Source: 7 bytes => {1,2,3,4,5,6,7} + uint8_t src[7]; + for( int i = 0 ; i < 7 ; i++ ){ + src[i] = (uint8_t)( i + 1 ); + } + + uint8_t dst[7]; + memset( dst ,0 ,7 ); + + Copy·it it; + it.read0 = src; // Base of read + it.read_size = 7; // 7 bytes total + it.write0 = dst; + it.write_size = 4; // Only 4 bytes can be written initially + + // 1st step: we copy the top 4 bytes in reverse => should be {7,6,5,4} + { + Copy·Status st = Copy·step_reverse_order( &it ); + if( st != Copy·Status·incomplete_read ){ + return false; + } + // Check that the first 4 reversed bytes ended up in dst + // We expect: dst[0]=7, dst[1]=6, dst[2]=5, dst[3]=4 + if( dst[0] != 7 || dst[1] != 6 || dst[2] != 5 || dst[3] != 4 ){ + return false; + } + // read_size should now be 3, write_size=0 + if( it.read_size != 3 || it.write_size != 0 ){ + return false; + } + } + + // Provide 3 more bytes of write space + it.write_size = 3; + { + // 2nd step: copy the remaining 3 bytes in reverse => {3,2,1} + Copy·Status st = Copy·step_reverse_order( &it ); + // With 3 left to read, 3 left to write => expect complete or incomplete_write + if( st != Copy·Status·complete ){ + return false; + } + // Check we wrote them after the first 4 + if( dst[4] != 3 || dst[5] != 2 || dst[6] != 1 ){ + return false; + } + // Now read_size=0, write_size=0 + if( it.read_size != 0 || it.write_size != 0 ){ + return false; + } + } + + return true; +} + +// for block box testing this goes at the bottom +// for white box testing this goes at the top + +#define LOCAL +#include "Copy.lib.c" +#endif diff --git "a/tester/cc\360\237\226\211/test_setup.cli.c" "b/tester/cc\360\237\226\211/test_setup.cli.c" index f1c6e68..eed666c 100644 --- "a/tester/cc\360\237\226\211/test_setup.cli.c" +++ "b/tester/cc\360\237\226\211/test_setup.cli.c" @@ -4,9 +4,10 @@ */ -#define IFACE +#define FACE #include #include +#undef FACE // No need to define IMPLEMENTATION as `main` is one and done. @@ -20,3 +21,7 @@ int main(int argc ,char *argv[] ,char *envp[]){ return 0; } + + +#define LOCAL +#undef LOCAL diff --git "a/tester/tool\360\237\226\211/release_pull" "b/tester/tool\360\237\226\211/release_pull" index d76d7e3..c79aca6 100755 --- "a/tester/tool\360\237\226\211/release_pull" +++ "b/tester/tool\360\237\226\211/release_pull" @@ -9,21 +9,21 @@ script_afp=$(realpath "${BASH_SOURCE[0]}") # input guards env_must_be="tester/tool🖉/env" if [ "$ENV" != "$env_must_be" ]; then - echo "$(script_afp):: error: must be run in the $env_must_be environment" + echo "$(script_fp):: error: must be run in the $env_must_be environment" exit 1 fi cd "$REPO_HOME"/tester || exit 1 if [ ! -d scratchpad ]; then - echo "$(script_afp):: no scratchpad directory" + echo "$(script_fp):: no scratchpad directory" exit 1 fi release_dir=$(release_dir) if [ ! -d "$release_dir" ]; then - echo "$(script_afp):: no release directory to pull install files from" + echo "$(script_fp):: no release directory to pull install files from" exit 1 fi @@ -32,7 +32,7 @@ log_file="scratchpad/release_files.log" # Copy release files and track them if ! cp -f "$release_dir/libN.a" scratchpad && realpath scratchpad/libN.a >> "$log_file"; then - echo "$(script_afp):: warning: no libN.a found" >&2 + echo "$(script_fp):: warning: no libN.a found" >&2 fi for file in "$release_dir"/*.lib.c; do @@ -40,11 +40,11 @@ for file in "$release_dir"/*.lib.c; do if cp -f "$file" cc; then realpath "cc/$(basename "$file")" >> "$log_file" else - echo "$(script_afp):: warning: failed to copy $file" >&2 + echo "$(script_fp):: warning: failed to copy $file" >&2 fi else - echo "$(script_afp):: warning: skipping non-regular file $file" >&2 + echo "$(script_fp):: warning: skipping non-regular file $file" >&2 fi done -echo "$(script_afp) done." +echo "$(script_fn) done." diff --git "a/tester/tool\360\237\226\211/release_remove" "b/tester/tool\360\237\226\211/release_remove" index 3fa7ede..9e91fa6 100755 --- "a/tester/tool\360\237\226\211/release_remove" +++ "b/tester/tool\360\237\226\211/release_remove" @@ -7,7 +7,7 @@ script_afp=$(realpath "${BASH_SOURCE[0]}") # Input guards env_must_be="tester/tool🖉/env" if [ "$ENV" != "$env_must_be" ]; then - echo "$(script_afp):: error: must be run in the $env_must_be environment" + echo "$(script_fp):: error: must be run in the $env_must_be environment" exit 1 fi @@ -17,7 +17,7 @@ log_file="scratchpad/release_files.log" # Ensure log file exists and is not empty if [ ! -s "$log_file" ]; then - echo "$(script_afp):: no release log found, nothing to remove." >&2 + echo "$(script_fp):: no release log found, nothing to remove." >&2 exit 0 fi @@ -26,11 +26,11 @@ while IFS= read -r file; do if [ -f "$file" ]; then rm_na "$file" else - echo "$(script_afp):: warning: expected release file not found: $file" >&2 + echo "$(script_fp):: warning: expected release file not found: $file" >&2 fi done < "$log_file" # Cleanup log file after successful removal : > "$log_file" -echo "$(script_afp) done." +echo "$(script_fn) done." -- 2.20.1