Line data Source code
1 : /**
2 : * @file pt_compat.c
3 : * @brief Cross-platform compatibility layer implementation
4 : */
5 :
6 : #include "pt_compat.h"
7 :
8 : #if defined(PT_PLATFORM_POSIX)
9 : #include <stdlib.h>
10 : #include <string.h>
11 : #include <stdio.h>
12 : #include <time.h> /* For clock_gettime in pt_get_ticks */
13 : #elif defined(PT_PLATFORM_MACTCP) || defined(PT_PLATFORM_OT)
14 : #include <MacMemory.h>
15 : #ifdef PT_PLATFORM_OT
16 : #include <OpenTransport.h>
17 : #endif
18 : #endif
19 :
20 : /* ========================================================================== */
21 : /* Memory Allocation - POSIX */
22 : /* ========================================================================== */
23 :
24 : #if defined(PT_PLATFORM_POSIX)
25 :
26 215 : void *pt_alloc(size_t size) {
27 215 : return malloc(size);
28 : }
29 :
30 633 : void pt_free(void *ptr) {
31 633 : free(ptr);
32 633 : }
33 :
34 635 : void *pt_alloc_clear(size_t size) {
35 635 : return calloc(1, size);
36 : }
37 :
38 1 : size_t pt_get_free_mem(void) {
39 : /* POSIX: return effectively unlimited */
40 1 : return 1024UL * 1024UL * 1024UL; /* 1GB */
41 : }
42 :
43 1 : size_t pt_get_max_block(void) {
44 : /* POSIX: return effectively unlimited */
45 1 : return 1024UL * 1024UL * 1024UL; /* 1GB */
46 : }
47 :
48 : #endif /* PT_PLATFORM_POSIX */
49 :
50 : /* ========================================================================== */
51 : /* Memory Allocation - Classic Mac */
52 : /* ========================================================================== */
53 :
54 : #if defined(PT_PLATFORM_MACTCP) || defined(PT_PLATFORM_OT)
55 :
56 : void *pt_alloc(size_t size) {
57 : return NewPtr((Size)size);
58 : }
59 :
60 : void pt_free(void *ptr) {
61 : if (ptr != NULL) {
62 : DisposePtr((Ptr)ptr);
63 : }
64 : }
65 :
66 : void *pt_alloc_clear(size_t size) {
67 : return NewPtrClear((Size)size);
68 : }
69 :
70 : size_t pt_get_free_mem(void) {
71 : return (size_t)FreeMem();
72 : }
73 :
74 : size_t pt_get_max_block(void) {
75 : return (size_t)MaxBlock();
76 : }
77 :
78 : #endif /* PT_PLATFORM_MACTCP || PT_PLATFORM_OT */
79 :
80 : /* ========================================================================== */
81 : /* Atomic Operations - POSIX */
82 : /* ========================================================================== */
83 :
84 : #if defined(PT_PLATFORM_POSIX)
85 :
86 : /**
87 : * IMPORTANT: POSIX 'atomic' operations - NOT thread-safe for true
88 : * multi-threading!
89 : *
90 : * PeerTalk uses a single-threaded, non-blocking event loop model.
91 : * These volatile operations are sufficient for that use case.
92 : *
93 : * For applications that use PeerTalk from multiple threads, external
94 : * synchronization (e.g., pthread_mutex) is required around PeerTalk
95 : * API calls.
96 : */
97 :
98 2 : void pt_atomic_set_bit(pt_atomic_t *flags, int bit) {
99 2 : *flags |= (1U << bit);
100 2 : }
101 :
102 1 : void pt_atomic_clear_bit(pt_atomic_t *flags, int bit) {
103 1 : *flags &= ~(1U << bit);
104 1 : }
105 :
106 7 : int pt_atomic_test_bit(const pt_atomic_t *flags, int bit) {
107 7 : return (*flags & (1U << bit)) != 0;
108 : }
109 :
110 2 : int pt_atomic_test_and_clear_bit(pt_atomic_t *flags, int bit) {
111 2 : uint32_t mask = (1U << bit);
112 2 : int was_set = (*flags & mask) != 0;
113 2 : *flags &= ~mask;
114 2 : return was_set;
115 : }
116 :
117 : #endif /* PT_PLATFORM_POSIX */
118 :
119 : /* ========================================================================== */
120 : /* Atomic Operations - MacTCP (68k) */
121 : /* ========================================================================== */
122 :
123 : #if defined(PT_PLATFORM_MACTCP)
124 :
125 : /**
126 : * 68k atomic operations using volatile.
127 : *
128 : * Safe because:
129 : * 1. ASR only SETS bits (via OR): *flags |= mask
130 : * 2. Main loop only CLEARS bits (via AND): *flags &= ~mask
131 : * 3. No read-modify-write race (operations are one-way)
132 : * 4. 32-bit aligned access is atomic on 68000+ (hardware guarantee)
133 : *
134 : * Based on Motorola 68000 User's Manual, Section 8:
135 : * "32-bit aligned accesses complete in a single bus cycle and cannot
136 : * be interrupted mid-operation."
137 : *
138 : * Does NOT disable interrupts - unnecessary for this pattern.
139 : */
140 :
141 : void pt_atomic_set_bit(pt_atomic_t *flags, int bit) {
142 : *flags |= (1U << bit);
143 : }
144 :
145 : void pt_atomic_clear_bit(pt_atomic_t *flags, int bit) {
146 : *flags &= ~(1U << bit);
147 : }
148 :
149 : int pt_atomic_test_bit(const pt_atomic_t *flags, int bit) {
150 : return (*flags & (1U << bit)) != 0;
151 : }
152 :
153 : int pt_atomic_test_and_clear_bit(pt_atomic_t *flags, int bit) {
154 : uint32_t mask = (1U << bit);
155 : int was_set = (*flags & mask) != 0;
156 : *flags &= ~mask;
157 : return was_set;
158 : }
159 :
160 : #endif /* PT_PLATFORM_MACTCP */
161 :
162 : /* ========================================================================== */
163 : /* Atomic Operations - Open Transport (PPC) */
164 : /* ========================================================================== */
165 :
166 : #if defined(PT_PLATFORM_OT)
167 :
168 : /**
169 : * Open Transport atomic operations using OTAtomic* functions.
170 : *
171 : * OTAtomic functions operate on BYTES with bit indices 0-7.
172 : * For a 32-bit flags word, we need to calculate which byte contains
173 : * the bit we want to manipulate.
174 : *
175 : * Big-endian byte layout (32-bit word at address 0x1000):
176 : * 0x1000: byte 0 (bits 24-31) - OT bits 0-7
177 : * 0x1001: byte 1 (bits 16-23) - OT bits 0-7
178 : * 0x1002: byte 2 (bits 8-15) - OT bits 0-7
179 : * 0x1003: byte 3 (bits 0-7) - OT bits 0-7
180 : *
181 : * To set logical bit N (0-31):
182 : * byte_offset = 3 - (N / 8)
183 : * bit_in_byte = N % 8
184 : */
185 :
186 : void pt_atomic_set_bit(pt_atomic_t *flags, int bit) {
187 : int byte_offset = 3 - (bit / 8);
188 : int bit_in_byte = bit % 8;
189 : UInt8 *byte_ptr = ((UInt8 *)flags) + byte_offset;
190 : OTAtomicSetBit(byte_ptr, (UInt8)bit_in_byte);
191 : }
192 :
193 : void pt_atomic_clear_bit(pt_atomic_t *flags, int bit) {
194 : int byte_offset = 3 - (bit / 8);
195 : int bit_in_byte = bit % 8;
196 : UInt8 *byte_ptr = ((UInt8 *)flags) + byte_offset;
197 : OTAtomicClearBit(byte_ptr, (UInt8)bit_in_byte);
198 : }
199 :
200 : int pt_atomic_test_bit(pt_atomic_t *flags, int bit) {
201 : int byte_offset = 3 - (bit / 8);
202 : int bit_in_byte = bit % 8;
203 : UInt8 *byte_ptr = ((UInt8 *)flags) + byte_offset;
204 : return OTAtomicTestBit(byte_ptr, (UInt8)bit_in_byte);
205 : }
206 :
207 : int pt_atomic_test_and_clear_bit(pt_atomic_t *flags, int bit) {
208 : int byte_offset = 3 - (bit / 8);
209 : int bit_in_byte = bit % 8;
210 : UInt8 *byte_ptr = ((UInt8 *)flags) + byte_offset;
211 : Boolean was_set = OTAtomicTestBit(byte_ptr, (UInt8)bit_in_byte);
212 : if (was_set) {
213 : OTAtomicClearBit(byte_ptr, (UInt8)bit_in_byte);
214 : }
215 : return was_set ? 1 : 0;
216 : }
217 :
218 : #endif /* PT_PLATFORM_OT */
219 :
220 : /* ========================================================================== */
221 : /* Memory Utilities - POSIX */
222 : /* ========================================================================== */
223 :
224 : #if defined(PT_PLATFORM_POSIX)
225 :
226 21727 : void *pt_memcpy(void *dest, const void *src, size_t n) {
227 21727 : return memcpy(dest, src, n);
228 : }
229 :
230 1124 : void *pt_memset(void *dest, int c, size_t n) {
231 1124 : return memset(dest, c, n);
232 : }
233 :
234 3 : int pt_memcmp(const void *a, const void *b, size_t n) {
235 3 : return memcmp(a, b, n);
236 : }
237 :
238 0 : void *pt_memmove(void *dest, const void *src, size_t n) {
239 0 : return memmove(dest, src, n);
240 : }
241 :
242 88 : size_t pt_strlen(const char *s) {
243 88 : return strlen(s);
244 : }
245 :
246 53 : char *pt_strncpy(char *dest, const char *src, size_t n) {
247 53 : strncpy(dest, src, n);
248 53 : if (n > 0) {
249 53 : dest[n - 1] = '\0'; /* Always null-terminate */
250 : }
251 53 : return dest;
252 : }
253 :
254 : #endif /* PT_PLATFORM_POSIX */
255 :
256 : /* ========================================================================== */
257 : /* Memory Utilities - Classic Mac */
258 : /* ========================================================================== */
259 :
260 : #if defined(PT_PLATFORM_MACTCP) || defined(PT_PLATFORM_OT)
261 :
262 : void *pt_memcpy(void *dest, const void *src, size_t n) {
263 : BlockMoveData(src, dest, (Size)n);
264 : return dest;
265 : }
266 :
267 : void *pt_memset(void *dest, int c, size_t n) {
268 : unsigned char *p = (unsigned char *)dest;
269 : unsigned char value = (unsigned char)c;
270 : size_t i;
271 : for (i = 0; i < n; i++) {
272 : p[i] = value;
273 : }
274 : return dest;
275 : }
276 :
277 : int pt_memcmp(const void *a, const void *b, size_t n) {
278 : const unsigned char *pa = (const unsigned char *)a;
279 : const unsigned char *pb = (const unsigned char *)b;
280 : size_t i;
281 : for (i = 0; i < n; i++) {
282 : if (pa[i] != pb[i]) {
283 : return (pa[i] < pb[i]) ? -1 : 1;
284 : }
285 : }
286 : return 0;
287 : }
288 :
289 : void *pt_memmove(void *dest, const void *src, size_t n) {
290 : unsigned char *d = (unsigned char *)dest;
291 : const unsigned char *s = (const unsigned char *)src;
292 :
293 : if (d == s || n == 0) {
294 : return dest;
295 : }
296 :
297 : if (d < s) {
298 : /* Forward copy (no overlap or dest before src) */
299 : size_t i;
300 : for (i = 0; i < n; i++) {
301 : d[i] = s[i];
302 : }
303 : } else {
304 : /* Backward copy (dest after src, may overlap) */
305 : size_t i = n;
306 : while (i > 0) {
307 : i--;
308 : d[i] = s[i];
309 : }
310 : }
311 : return dest;
312 : }
313 :
314 : size_t pt_strlen(const char *s) {
315 : size_t len = 0;
316 : while (*s++) {
317 : len++;
318 : }
319 : return len;
320 : }
321 :
322 : char *pt_strncpy(char *dest, const char *src, size_t n) {
323 : size_t i;
324 : for (i = 0; i < n && src[i] != '\0'; i++) {
325 : dest[i] = src[i];
326 : }
327 : /* Pad with nulls if src is shorter than n */
328 : for (; i < n; i++) {
329 : dest[i] = '\0';
330 : }
331 : /* Always null-terminate if buffer size > 0 */
332 : if (n > 0) {
333 : dest[n - 1] = '\0';
334 : }
335 : return dest;
336 : }
337 :
338 : #endif /* PT_PLATFORM_MACTCP || PT_PLATFORM_OT */
339 :
340 : /* ========================================================================== */
341 : /* ISR-Safe Memory Copy (All Platforms) */
342 : /* ========================================================================== */
343 :
344 2 : void *pt_memcpy_isr(void *dest, const void *src, size_t n) {
345 2 : unsigned char *d = (unsigned char *)dest;
346 2 : const unsigned char *s = (const unsigned char *)src;
347 : size_t i;
348 21 : for (i = 0; i < n; i++) {
349 19 : d[i] = s[i];
350 : }
351 2 : return dest;
352 : }
353 :
354 : /* ========================================================================== */
355 : /* Formatted Output - POSIX */
356 : /* ========================================================================== */
357 :
358 : #if defined(PT_PLATFORM_POSIX)
359 :
360 0 : int pt_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) {
361 0 : return vsnprintf(buf, size, fmt, args);
362 : }
363 :
364 18 : int pt_snprintf(char *buf, size_t size, const char *fmt, ...) {
365 : va_list args;
366 : int result;
367 18 : va_start(args, fmt);
368 18 : result = vsnprintf(buf, size, fmt, args);
369 18 : va_end(args);
370 18 : return result;
371 : }
372 :
373 : #endif /* PT_PLATFORM_POSIX */
374 :
375 : /* ========================================================================== */
376 : /* Formatted Output - Classic Mac (Limited Implementation) */
377 : /* ========================================================================== */
378 :
379 : #if defined(PT_PLATFORM_MACTCP) || defined(PT_PLATFORM_OT)
380 :
381 : /**
382 : * Helper: Format an integer to a string buffer.
383 : * @param value Integer value
384 : * @param base Base (10 or 16)
385 : * @param width Field width (0 = no padding)
386 : * @param zero_pad 1 to pad with zeros, 0 to pad with spaces
387 : * @param uppercase 1 for uppercase hex (A-F), 0 for lowercase (a-f)
388 : * @param is_signed 1 if value should be treated as signed
389 : * @param buf Output buffer
390 : * @param bufsize Size of output buffer
391 : * @return Number of characters written
392 : */
393 : static int pt_format_int(long value, int base, int width, int zero_pad,
394 : int uppercase, int is_signed, char *buf,
395 : size_t bufsize) {
396 : char temp[32];
397 : int len = 0;
398 : int negative = 0;
399 : unsigned long uvalue;
400 : const char *digits = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
401 :
402 : if (bufsize == 0) return 0;
403 :
404 : /* Handle signed values */
405 : if (is_signed && value < 0) {
406 : negative = 1;
407 : uvalue = (unsigned long)(-value);
408 : } else {
409 : uvalue = (unsigned long)value;
410 : }
411 :
412 : /* Convert to string (reversed) */
413 : if (uvalue == 0) {
414 : temp[len++] = '0';
415 : } else {
416 : while (uvalue > 0 && len < (int)sizeof(temp)) {
417 : /* cppcheck-suppress zerodivcond ; base is always 10 or 16 from callers */
418 : temp[len++] = digits[uvalue % base];
419 : uvalue /= base;
420 : }
421 : }
422 :
423 : /* Add sign if negative */
424 : if (negative) {
425 : temp[len++] = '-';
426 : }
427 :
428 : /* Calculate padding */
429 : int pad = width - len;
430 : if (pad < 0) pad = 0;
431 :
432 : int pos = 0;
433 :
434 : /* Pad with spaces (if not zero-padding) */
435 : if (!zero_pad) {
436 : while (pad > 0 && pos < (int)bufsize - 1) {
437 : buf[pos++] = ' ';
438 : pad--;
439 : }
440 : }
441 :
442 : /* If zero-padding and negative, write sign first */
443 : if (zero_pad && negative && pos < (int)bufsize - 1) {
444 : buf[pos++] = '-';
445 : len--; /* Sign already written */
446 : }
447 :
448 : /* Pad with zeros */
449 : if (zero_pad) {
450 : while (pad > 0 && pos < (int)bufsize - 1) {
451 : buf[pos++] = '0';
452 : pad--;
453 : }
454 : }
455 :
456 : /* Write digits (reversed) */
457 : int start = negative && zero_pad ? 1 : (negative ? 1 : 0);
458 : for (int i = len - 1; i >= start && pos < (int)bufsize - 1; i--) {
459 : buf[pos++] = temp[i];
460 : }
461 :
462 : buf[pos] = '\0';
463 : return pos;
464 : }
465 :
466 : int pt_vsnprintf(char *buf, size_t size, const char *fmt, va_list args) {
467 : size_t pos = 0;
468 : const char *p = fmt;
469 :
470 : if (size == 0) return 0;
471 :
472 : while (*p && pos < size - 1) {
473 : if (*p != '%') {
474 : buf[pos++] = *p++;
475 : continue;
476 : }
477 :
478 : p++; /* Skip '%' */
479 :
480 : /* Parse flags and width */
481 : int zero_pad = 0;
482 : int width = 0;
483 :
484 : if (*p == '0') {
485 : zero_pad = 1;
486 : p++;
487 : }
488 :
489 : while (*p >= '0' && *p <= '9') {
490 : width = width * 10 + (*p - '0');
491 : p++;
492 : }
493 :
494 : /* Parse length modifier */
495 : int is_long = 0;
496 : if (*p == 'l') {
497 : is_long = 1;
498 : p++;
499 : }
500 :
501 : /* Parse format specifier */
502 : switch (*p) {
503 : case 'd': {
504 : long val = is_long ? va_arg(args, long) : va_arg(args, int);
505 : int written = pt_format_int(val, 10, width, zero_pad, 0, 1,
506 : buf + pos, size - pos);
507 : pos += written;
508 : break;
509 : }
510 : case 'u': {
511 : unsigned long val = is_long ? va_arg(args, unsigned long)
512 : : va_arg(args, unsigned int);
513 : int written = pt_format_int((long)val, 10, width, zero_pad,
514 : 0, 0, buf + pos, size - pos);
515 : pos += written;
516 : break;
517 : }
518 : case 'x': {
519 : unsigned long val = is_long ? va_arg(args, unsigned long)
520 : : va_arg(args, unsigned int);
521 : int written = pt_format_int((long)val, 16, width, zero_pad,
522 : 0, 0, buf + pos, size - pos);
523 : pos += written;
524 : break;
525 : }
526 : case 'X': {
527 : unsigned long val = is_long ? va_arg(args, unsigned long)
528 : : va_arg(args, unsigned int);
529 : int written = pt_format_int((long)val, 16, width, zero_pad,
530 : 1, 0, buf + pos, size - pos);
531 : pos += written;
532 : break;
533 : }
534 : case 'p': {
535 : void *ptr = va_arg(args, void *);
536 : if (pos < size - 3) {
537 : buf[pos++] = '0';
538 : buf[pos++] = 'x';
539 : }
540 : int written = pt_format_int((long)ptr, 16, 8, 1, 0, 0,
541 : buf + pos, size - pos);
542 : pos += written;
543 : break;
544 : }
545 : case 's': {
546 : const char *str = va_arg(args, const char *);
547 : if (str == NULL) str = "(null)";
548 : while (*str && pos < size - 1) {
549 : buf[pos++] = *str++;
550 : }
551 : break;
552 : }
553 : case 'c': {
554 : char c = (char)va_arg(args, int);
555 : if (pos < size - 1) {
556 : buf[pos++] = c;
557 : }
558 : break;
559 : }
560 : case '%': {
561 : if (pos < size - 1) {
562 : buf[pos++] = '%';
563 : }
564 : break;
565 : }
566 : default:
567 : /* Unknown format - just copy it */
568 : if (pos < size - 1) {
569 : buf[pos++] = '%';
570 : }
571 : if (pos < size - 1 && *p) {
572 : buf[pos++] = *p;
573 : }
574 : break;
575 : }
576 :
577 : if (*p) p++;
578 : }
579 :
580 : buf[pos] = '\0';
581 : return (int)pos;
582 : }
583 :
584 : int pt_snprintf(char *buf, size_t size, const char *fmt, ...) {
585 : va_list args;
586 : int result;
587 : va_start(args, fmt);
588 : result = pt_vsnprintf(buf, size, fmt, args);
589 : va_end(args);
590 : return result;
591 : }
592 :
593 : #endif /* PT_PLATFORM_MACTCP || PT_PLATFORM_OT */
594 :
595 : /* ========================================================================== */
596 : /* Platform-Portable Tick Getter (Phase 3) */
597 : /* ========================================================================== */
598 :
599 : /*
600 : * Platform-portable tick getter
601 : *
602 : * Returns monotonically increasing tick count.
603 : * Resolution varies by platform but sufficient for coalescing/priority.
604 : */
605 : #if defined(PT_PLATFORM_MACTCP) || defined(PT_PLATFORM_OT)
606 : /* Classic Mac - use TickCount() directly (60 ticks/sec)
607 : * TickCount() is declared in OSUtils.h, NOT Timer.h
608 : * NOTE: TickCount() is NOT documented as interrupt-safe in Inside Macintosh
609 : * Table B-3. Do NOT call from ASR/notifier - use timestamp=0 instead.
610 : */
611 : #include <OSUtils.h>
612 : uint32_t pt_get_ticks(void) {
613 : return (uint32_t)TickCount();
614 : }
615 : #else
616 : /* POSIX - use milliseconds */
617 91 : uint32_t pt_get_ticks(void) {
618 : struct timespec ts;
619 91 : clock_gettime(CLOCK_MONOTONIC, &ts);
620 91 : return (uint32_t)(ts.tv_sec * 1000 + ts.tv_nsec / 1000000);
621 : }
622 : #endif
|