|
16 | 16 | --- |
17 | 17 |
|
18 | 18 | ## 1. File Naming and Structure |
19 | | -**Note:** All public types (usually via typedef) should be named with a `_t` suffix (e.g., `ptk_buf_t`, `ptk_err_t`). |
20 | | -Some existing types in `ptk_defs.h` do not follow this rule for historical reasons (e.g., `ptk_u8_t` should be `ptk_u8_t`). New public types must follow the `_t` naming convention. |
| 19 | + |
| 20 | +**Note:** All public types (usually via typedef) must be named with a `_t` suffix (e.g., `ptk_buf_t`, `ptk_err_t`, `ptk_slice_bytes_t`). |
| 21 | +All public types and functions must use the `ptk_` prefix. Internal/private types and functions must not use the `ptk_` prefix. |
| 22 | +New public types must follow the `_t` naming convention. Implementation-specific or private structs should be forward declared in public headers and defined only in the corresponding implementation file. |
| 23 | + |
| 24 | +**Implementation-specific header note:** |
| 25 | +If a struct's size or layout is platform-dependent, the public header should forward-declare the struct and include the implementation-specific header (e.g., `#include "posix/ptk_x_impl.h"`) in the `.c` file to ensure correct sizing and layout. This allows the public API to remain portable and opaque while the implementation can use the correct platform-specific details. |
21 | 26 |
|
22 | 27 | - **Public API files** use the `ptk_` prefix (e.g., `ptk_sock.h`, `ptk_mem.c`). |
23 | 28 | - **Private/implementation files** must NOT use the `ptk_` prefix, and no platform-specific suffix (e.g., `os_thread.c`, `linux/address.c`). |
@@ -56,67 +61,30 @@ Some existing types in `ptk_defs.h` do not follow this rule for historical reaso |
56 | 61 |
|
57 | 62 | --- |
58 | 63 |
|
| 64 | + |
| 65 | + |
59 | 66 | ## 4. Memory Management |
60 | 67 |
|
61 | | -- All thread-local memory allocation must use `ptk_local_alloc()` (not `malloc()`/`free()`). |
62 | | -- All data to be accessed by more than one thread must use `ptk_shared_alloc()` and use the `use_shared()` helper macro to avoid many errors with shared memory and locks. |
63 | | -- Always provide a destructor if any attached data needs to be freed or disposed of safely. |
64 | | -- If you find code like: |
65 | | - ```c |
66 | | - if(obj->field) { ptk_free(obj->field); } |
67 | | - ptk_free(obj); |
68 | | - ``` |
69 | | - then a destructor should have been provided at allocation. |
70 | | -- Do not provide public destructors; use the destructor callback with `ptk_local_alloc()` and `ptk_shared_alloc()`. |
71 | | -- All local memory should be freed with `ptk_local_free()`. |
72 | | -- All shared memory should be freed with `pkt_shared_free()`. |
| 68 | +- All memory allocation for temporary/thread-local data must use the scratch allocator API (`ptk_scratch_t*`, see `ptk_scratch.h`). Do not use `malloc()`/`free()` directly. |
| 69 | +- Use `ptk_scratch_alloc()`, `ptk_scratch_alloc_aligned()`, `ptk_scratch_alloc_slice()`, or `ptk_slice_alloc()` for all buffer and array allocations. |
| 70 | +- For persistent or shared data, use explicit user-provided buffers or platform-appropriate allocation APIs as required by the public API. |
| 71 | +- Resource cleanup should be handled by the allocator or by explicit API calls; there is no destructor callback in the public API. |
73 | 72 | - Ownership rules: |
74 | | - - If a function returns a pointer, the caller owns it and must free it with `ptk_local_free()`. |
| 73 | + - If a function returns a pointer or slice, the caller owns it and must release it using the appropriate allocator or by resetting the scratch allocator. |
75 | 74 | - If a function takes a pointer-to-pointer, it owns the value and will null out your pointer. |
76 | 75 |
|
77 | | -**IMPORTANT: When using `use_shared()` or `on_shared_fail` blocks, you MUST NOT use `return` to exit the block. Only `break` is allowed for early exit.** |
78 | | -
|
79 | | -> **WARNING:** |
80 | | -> Using `return` inside a `use_shared()` or `on_shared_fail` block will result in undefined behavior and may leak resources. The macro relies on structured control flow to ensure proper resource release. Always use `break` to exit these blocks early. |
81 | | -
|
82 | | -**Example (correct):** |
83 | | -```c |
84 | | -use_shared(handle, 100, my_struct_t *ptr) { |
85 | | - if(ptr->field == 0) { |
86 | | - break; // OK: releases resources properly |
87 | | - } |
88 | | - // ... |
89 | | -} on_shared_fail { |
90 | | - // ... |
91 | | -} |
92 | | -``` |
93 | | - |
94 | | -**Example (incorrect, do NOT do this):** |
95 | | -```c |
96 | | -use_shared(handle, 100, my_struct_t *ptr) { |
97 | | - if(ptr->field == 0) { |
98 | | - return; // ERROR: do NOT use return here! |
99 | | - } |
100 | | - // ... |
101 | | -} on_shared_fail { |
102 | | - // ... |
103 | | -} |
104 | | -``` |
105 | | -
|
106 | | -This restriction is required for correct resource management! |
107 | | -
|
108 | 76 | --- |
109 | 77 |
|
| 78 | + |
| 79 | + |
| 80 | + |
110 | 81 | ## 5. Logging and Debugging |
111 | 82 |
|
112 | | -- Use the logging functions/macros in `ptk_log.h` for all logging. |
113 | | -- Do not include function names or line numbers in log messages (the macros do this automatically). |
114 | | -- Log levels: |
115 | | - - `info()`: function entry/exit |
116 | | - - `debug()`: important steps within functions |
117 | | - - `warn()`: recoverable failures (e.g., out of file descriptors) |
118 | | - - `error()`: catastrophic failures (e.g., allocation failure) |
119 | | - - `trace()`: very frequent events (e.g., tight loops, frequent callbacks) |
| 83 | +- Use the logging macros in `ptk_log.h` for all logging. The available macros are: |
| 84 | + - `PTK_LOG(fmt, ...)` for general logging with file, function, and line number automatically included. |
| 85 | + - `PTK_LOG_SLICE(slice)` to print a byte slice as hex with context. |
| 86 | +- There are no separate log levels; use `PTK_LOG` for all log messages. |
| 87 | +- Do not include function names or line numbers in log messages; the macro adds these automatically. |
120 | 88 |
|
121 | 89 | --- |
122 | 90 |
|
@@ -154,16 +122,15 @@ This restriction is required for correct resource management! |
154 | 122 |
|
155 | 123 | --- |
156 | 124 |
|
| 125 | + |
| 126 | + |
157 | 127 | ## 8. General Coding Practices |
158 | 128 |
|
159 | | -- All code must be bounds-checked; avoid pointer arithmetic. |
160 | | -- Use safe array/buffer accessors. |
161 | | -- Do not store raw pointers from shared memory beyond their acquire/release block. |
162 | | -- Handle reference count overflow gracefully in shared memory APIs. |
163 | | -- Shared memory is thread-safe, but the data it points to may not be—add your own synchronization if needed. |
| 129 | +- All code must be bounds-checked; avoid pointer arithmetic. Use the type-safe slice system (`ptk_slice_*_t` and helpers) for all buffer and array access. |
| 130 | +- Use the provided slice macros and functions (see `ptk_slice.h`) for safe, type-checked access to arrays and buffers. |
| 131 | +- Do not store raw pointers from shared memory or buffers beyond their valid lifetime. |
164 | 132 | - Internal implementation functions and data definitions within a `.c` file should always be `static`. |
165 | 133 | - Header files that declare public functions and data must use `extern` explicitly. |
166 | | -- Static inline functions are typesafe. Macros are not. Do not use macros when it would be just as easy to add a static inline function. |
167 | | -- Use the safe array template. |
| 134 | +- Use static inline functions for type safety where possible. Macros are used for slice and serialization helpers where appropriate. |
168 | 135 |
|
169 | 136 | --- |
0 commit comments