2424 #error "Atomic operations not available on this platform"
2525#endif
2626
27+ // Byte swap functions (for endianness conversion)
28+ static inline uint16_t swap16 (uint16_t x ) {
29+ return ((x & 0xFF00 ) >> 8 ) | ((x & 0x00FF ) << 8 );
30+ }
31+
32+ static inline uint32_t swap32 (uint32_t x ) {
33+ return ((x & 0xFF000000 ) >> 24 ) | ((x & 0x00FF0000 ) >> 8 ) |
34+ ((x & 0x0000FF00 ) << 8 ) | ((x & 0x000000FF ) << 24 );
35+ }
36+
37+ static inline uint64_t swap64 (uint64_t x ) {
38+ return ((x & 0xFF00000000000000ULL ) >> 56 ) | ((x & 0x00FF000000000000ULL ) >> 40 ) |
39+ ((x & 0x0000FF0000000000ULL ) >> 24 ) | ((x & 0x000000FF00000000ULL ) >> 8 ) |
40+ ((x & 0x00000000FF000000ULL ) << 8 ) | ((x & 0x0000000000FF0000ULL ) << 24 ) |
41+ ((x & 0x000000000000FF00ULL ) << 40 ) | ((x & 0x00000000000000FFULL ) << 56 );
42+ }
43+
2744// Helper to get buffer pointer and validate
2845static void * get_buffer_pointer (VALUE buffer , size_t offset , size_t size ) {
2946 void * base ;
@@ -81,6 +98,40 @@ static ID RB_IO_BUFFER_DATA_TYPE_S64;
8198 return val2num(result); \
8299 }
83100
101+ // Helper macro for swap function selection based on size
102+ #define SWAP_BY_SIZE (size_val , value ) \
103+ ((size_val) == 8 ? (uint8_t)(value) : \
104+ (size_val) == 16 ? swap16((uint16_t)(value)) : \
105+ (size_val) == 32 ? swap32((uint32_t)(value)) : \
106+ swap64((uint64_t)(value)))
107+
108+ // Macro for atomic load (with endianness conversion)
109+ #define ATOMIC_LOAD_IMPL (name , ctype , atomic_type , size_bits , num2val , val2num , endian , swap_func ) \
110+ static VALUE atomic_load_##name(VALUE self, size_t offset) { \
111+ void *pointer = get_buffer_pointer(self, offset, size_bits / 8); \
112+ ctype result = atomic_load((atomic_type*)pointer); \
113+ if (endian != RB_IO_BUFFER_HOST_ENDIAN && size_bits > 8) { \
114+ if (size_bits == 16) result = (ctype)swap16((uint16_t)result); \
115+ else if (size_bits == 32) result = (ctype)swap32((uint32_t)result); \
116+ else if (size_bits == 64) result = (ctype)swap64((uint64_t)result); \
117+ } \
118+ return val2num(result); \
119+ }
120+
121+ // Macro for atomic store (with endianness conversion)
122+ #define ATOMIC_STORE_IMPL (name , ctype , atomic_type , size_bits , num2val , val2num , endian , swap_func ) \
123+ static VALUE atomic_store_##name(VALUE self, size_t offset, VALUE value) { \
124+ void *pointer = get_buffer_pointer(self, offset, size_bits / 8); \
125+ ctype converted_value = (ctype)num2val(value); \
126+ if (endian != RB_IO_BUFFER_HOST_ENDIAN && size_bits > 8) { \
127+ if (size_bits == 16) converted_value = (ctype)swap16((uint16_t)converted_value); \
128+ else if (size_bits == 32) converted_value = (ctype)swap32((uint32_t)converted_value); \
129+ else if (size_bits == 64) converted_value = (ctype)swap64((uint64_t)converted_value); \
130+ } \
131+ atomic_store((atomic_type*)pointer, converted_value); \
132+ return self; \
133+ }
134+
84135// Macro for atomic compare and swap
85136#define ATOMIC_CAS_IMPL (name , ctype , atomic_type , size , num2val , val2num ) \
86137 static int atomic_cas_##name(VALUE self, size_t offset, VALUE expected_value, VALUE desired_value) { \
@@ -128,6 +179,46 @@ ATOMIC_BITWISE_IMPL(i32, int32_t, atomic_int_least32_t, 4, NUM2INT, INT2NUM, xor
128179ATOMIC_BITWISE_IMPL (u64 , uint64_t , atomic_uint_least64_t , 8 , NUM2ULL , ULL2NUM , xor )
129180ATOMIC_BITWISE_IMPL (i64 , int64_t , atomic_int_least64_t , 8 , NUM2LL , LL2NUM , xor )
130181
182+ // 8-bit types: no swap needed (single byte), Ruby only defines U8/S8 (big-endian)
183+ ATOMIC_LOAD_IMPL (U8 , uint8_t , atomic_uint_least8_t , 8 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_BIG_ENDIAN , NULL )
184+ ATOMIC_LOAD_IMPL (S8 , int8_t , atomic_int_least8_t , 8 , NUM2INT , INT2NUM , RB_IO_BUFFER_BIG_ENDIAN , NULL )
185+
186+ // 16-bit types
187+ ATOMIC_LOAD_IMPL (u16 , uint16_t , atomic_uint_least16_t , 16 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap16 )
188+ ATOMIC_LOAD_IMPL (s16 , int16_t , atomic_int_least16_t , 16 , NUM2INT , INT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap16 )
189+ ATOMIC_LOAD_IMPL (U16 , uint16_t , atomic_uint_least16_t , 16 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap16 )
190+ ATOMIC_LOAD_IMPL (S16 , int16_t , atomic_int_least16_t , 16 , NUM2INT , INT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap16 )
191+
192+ // 32-bit types
193+ ATOMIC_LOAD_IMPL (u32 , uint32_t , atomic_uint_least32_t , 32 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap32 )
194+ ATOMIC_LOAD_IMPL (s32 , int32_t , atomic_int_least32_t , 32 , NUM2INT , INT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap32 )
195+ ATOMIC_LOAD_IMPL (U32 , uint32_t , atomic_uint_least32_t , 32 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap32 )
196+ ATOMIC_LOAD_IMPL (S32 , int32_t , atomic_int_least32_t , 32 , NUM2INT , INT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap32 )
197+
198+ // 64-bit types
199+ ATOMIC_LOAD_IMPL (u64 , uint64_t , atomic_uint_least64_t , 64 , NUM2ULL , ULL2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap64 )
200+ ATOMIC_LOAD_IMPL (s64 , int64_t , atomic_int_least64_t , 64 , NUM2LL , LL2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap64 )
201+ ATOMIC_LOAD_IMPL (U64 , uint64_t , atomic_uint_least64_t , 64 , NUM2ULL , ULL2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap64 )
202+ ATOMIC_LOAD_IMPL (S64 , int64_t , atomic_int_least64_t , 64 , NUM2LL , LL2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap64 )
203+
204+ ATOMIC_STORE_IMPL (U8 , uint8_t , atomic_uint_least8_t , 8 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_BIG_ENDIAN , NULL )
205+ ATOMIC_STORE_IMPL (S8 , int8_t , atomic_int_least8_t , 8 , NUM2INT , INT2NUM , RB_IO_BUFFER_BIG_ENDIAN , NULL )
206+
207+ ATOMIC_STORE_IMPL (u16 , uint16_t , atomic_uint_least16_t , 16 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap16 )
208+ ATOMIC_STORE_IMPL (s16 , int16_t , atomic_int_least16_t , 16 , NUM2INT , INT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap16 )
209+ ATOMIC_STORE_IMPL (U16 , uint16_t , atomic_uint_least16_t , 16 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap16 )
210+ ATOMIC_STORE_IMPL (S16 , int16_t , atomic_int_least16_t , 16 , NUM2INT , INT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap16 )
211+
212+ ATOMIC_STORE_IMPL (u32 , uint32_t , atomic_uint_least32_t , 32 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap32 )
213+ ATOMIC_STORE_IMPL (s32 , int32_t , atomic_int_least32_t , 32 , NUM2INT , INT2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap32 )
214+ ATOMIC_STORE_IMPL (U32 , uint32_t , atomic_uint_least32_t , 32 , NUM2UINT , UINT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap32 )
215+ ATOMIC_STORE_IMPL (S32 , int32_t , atomic_int_least32_t , 32 , NUM2INT , INT2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap32 )
216+
217+ ATOMIC_STORE_IMPL (u64 , uint64_t , atomic_uint_least64_t , 64 , NUM2ULL , ULL2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap64 )
218+ ATOMIC_STORE_IMPL (s64 , int64_t , atomic_int_least64_t , 64 , NUM2LL , LL2NUM , RB_IO_BUFFER_LITTLE_ENDIAN , swap64 )
219+ ATOMIC_STORE_IMPL (U64 , uint64_t , atomic_uint_least64_t , 64 , NUM2ULL , ULL2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap64 )
220+ ATOMIC_STORE_IMPL (S64 , int64_t , atomic_int_least64_t , 64 , NUM2LL , LL2NUM , RB_IO_BUFFER_BIG_ENDIAN , swap64 )
221+
131222ATOMIC_CAS_IMPL (u8 , uint8_t , atomic_uint_least8_t , 1 , NUM2UINT , UINT2NUM )
132223ATOMIC_CAS_IMPL (i8 , int8_t , atomic_int_least8_t , 1 , NUM2INT , INT2NUM )
133224ATOMIC_CAS_IMPL (u16 , uint16_t , atomic_uint_least16_t , 2 , NUM2UINT , UINT2NUM )
@@ -242,6 +333,58 @@ static VALUE atomic_xor_impl(VALUE self, ID type_identifier, size_t offset, long
242333 return Qnil ;
243334}
244335
336+ static VALUE atomic_load_impl (VALUE self , ID type_identifier , size_t offset ) {
337+ #define ATOMIC_LOAD_CASE (name , impl_name ) \
338+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
339+ return atomic_load_##impl_name(self, offset); \
340+ }
341+
342+ ATOMIC_LOAD_CASE (U8 , U8 )
343+ ATOMIC_LOAD_CASE (S8 , S8 )
344+ ATOMIC_LOAD_CASE (u16 , u16 )
345+ ATOMIC_LOAD_CASE (s16 , s16 )
346+ ATOMIC_LOAD_CASE (U16 , U16 )
347+ ATOMIC_LOAD_CASE (S16 , S16 )
348+ ATOMIC_LOAD_CASE (u32 , u32 )
349+ ATOMIC_LOAD_CASE (s32 , s32 )
350+ ATOMIC_LOAD_CASE (U32 , U32 )
351+ ATOMIC_LOAD_CASE (S32 , S32 )
352+ ATOMIC_LOAD_CASE (u64 , u64 )
353+ ATOMIC_LOAD_CASE (s64 , s64 )
354+ ATOMIC_LOAD_CASE (U64 , U64 )
355+ ATOMIC_LOAD_CASE (S64 , S64 )
356+ #undef ATOMIC_LOAD_CASE
357+
358+ rb_raise (rb_eArgError , "Unsupported type for atomic operations: %" PRIsVALUE , rb_id2str (type_identifier ));
359+ return Qnil ;
360+ }
361+
362+ static VALUE atomic_store_impl (VALUE self , ID type_identifier , size_t offset , VALUE value ) {
363+ #define ATOMIC_STORE_CASE (name , impl_name ) \
364+ if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
365+ return atomic_store_##impl_name(self, offset, value); \
366+ }
367+
368+ ATOMIC_STORE_CASE (U8 , U8 )
369+ ATOMIC_STORE_CASE (S8 , S8 )
370+ ATOMIC_STORE_CASE (u16 , u16 )
371+ ATOMIC_STORE_CASE (s16 , s16 )
372+ ATOMIC_STORE_CASE (U16 , U16 )
373+ ATOMIC_STORE_CASE (S16 , S16 )
374+ ATOMIC_STORE_CASE (u32 , u32 )
375+ ATOMIC_STORE_CASE (s32 , s32 )
376+ ATOMIC_STORE_CASE (U32 , U32 )
377+ ATOMIC_STORE_CASE (S32 , S32 )
378+ ATOMIC_STORE_CASE (u64 , u64 )
379+ ATOMIC_STORE_CASE (s64 , s64 )
380+ ATOMIC_STORE_CASE (U64 , U64 )
381+ ATOMIC_STORE_CASE (S64 , S64 )
382+ #undef ATOMIC_STORE_CASE
383+
384+ rb_raise (rb_eArgError , "Unsupported type for atomic operations: %" PRIsVALUE , rb_id2str (type_identifier ));
385+ return Qnil ;
386+ }
387+
245388static VALUE atomic_compare_and_swap_impl (VALUE self , ID type_identifier , size_t offset , VALUE expected_value , VALUE desired_value ) {
246389#define ATOMIC_CAS_CASE (name , impl_name ) \
247390 if (type_identifier == RB_IO_BUFFER_DATA_TYPE_##name) { \
@@ -347,6 +490,20 @@ static VALUE atomic_xor(VALUE self, VALUE type_symbol, VALUE offset_value, VALUE
347490 return atomic_xor_impl (self , type_identifier , offset , value );
348491}
349492
493+ static VALUE atomic_load_method (VALUE self , VALUE type_symbol , VALUE offset_value ) {
494+ ID type_identifier = get_type_id (type_symbol );
495+ size_t offset = NUM2SIZET (offset_value );
496+
497+ return atomic_load_impl (self , type_identifier , offset );
498+ }
499+
500+ static VALUE atomic_store_method (VALUE self , VALUE type_symbol , VALUE offset_value , VALUE value ) {
501+ ID type_identifier = get_type_id (type_symbol );
502+ size_t offset = NUM2SIZET (offset_value );
503+
504+ return atomic_store_impl (self , type_identifier , offset , value );
505+ }
506+
350507static VALUE atomic_compare_and_swap (VALUE self , VALUE type_symbol , VALUE offset_value , VALUE expected_value , VALUE desired_value ) {
351508 ID type_identifier = get_type_id (type_symbol );
352509 size_t offset = NUM2SIZET (offset_value );
@@ -371,7 +528,7 @@ void Init_IO_Buffer_Atomic(void) {
371528
372529 VALUE IO_Buffer = rb_const_get (rb_cIO , rb_intern ("Buffer" ));
373530
374- // Initialize type IDs (matching Ruby's pattern )
531+ // Initialize type IDs (support both forms for compatibility, but atomic operations work on raw bytes in host endian )
375532#define IO_BUFFER_DEFINE_DATA_TYPE (name ) RB_IO_BUFFER_DATA_TYPE_##name = rb_intern_const(#name)
376533 IO_BUFFER_DEFINE_DATA_TYPE (U8 );
377534 IO_BUFFER_DEFINE_DATA_TYPE (S8 );
@@ -396,5 +553,7 @@ void Init_IO_Buffer_Atomic(void) {
396553 DEFINE_METHOD_IF_NOT_EXISTS (IO_Buffer , "atomic_and" , atomic_and , 3 );
397554 DEFINE_METHOD_IF_NOT_EXISTS (IO_Buffer , "atomic_or" , atomic_or , 3 );
398555 DEFINE_METHOD_IF_NOT_EXISTS (IO_Buffer , "atomic_xor" , atomic_xor , 3 );
556+ DEFINE_METHOD_IF_NOT_EXISTS (IO_Buffer , "atomic_load" , atomic_load_method , 2 );
557+ DEFINE_METHOD_IF_NOT_EXISTS (IO_Buffer , "atomic_store" , atomic_store_method , 3 );
399558 DEFINE_METHOD_IF_NOT_EXISTS (IO_Buffer , "atomic_compare_and_swap" , atomic_compare_and_swap , 4 );
400559}
0 commit comments