Created
May 7, 2026 22:35
-
-
Save DeadlySurgeon/86b0a735d2f2b4ddc1f1da97fdf170cf to your computer and use it in GitHub Desktop.
Ring Buffer
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| %define RB_CAPACITY (50) | |
| struc ring_buffer | |
| .head: resq 1 | |
| .tail: resq 1 | |
| .limit: resq 1 | |
| .buffer: resb RB_CAPACITY | |
| endstruc | |
| section .data | |
| read_rb: | |
| istruc ring_buffer | |
| at ring_buffer.head, dq 0 | |
| at ring_buffer.tail, dq 0 | |
| at ring_buffer.limit, dq RB_CAPACITY | |
| at ring_buffer.buffer, times RB_CAPACITY db 0 | |
| iend | |
| write_rb: | |
| istruc ring_buffer | |
| at ring_buffer.head, dq 0 | |
| at ring_buffer.tail, dq 0 | |
| at ring_buffer.limit, dq RB_CAPACITY | |
| at ring_buffer.buffer, times RB_CAPACITY db 0 | |
| iend | |
| section .text | |
| ; Input: | |
| ; %rdi = &ring_buffer | |
| ; Output: | |
| ; %rax = pointer | |
| ; %rdx = length | |
| ; Clobbers: | |
| ; %r8 | |
| ; %r9 | |
| ; %r10 | |
| rb_read_span: | |
| mov r8, [rdi + ring_buffer.head] ; Head | |
| mov r9, [rdi + ring_buffer.tail] ; Tail | |
| mov qword r10, [rdi + ring_buffer.limit] | |
| ; If the tail is at the limit, skip padding and wrap. | |
| cmp r9, r10 | |
| jne .tail_ok | |
| xor r9, r9 | |
| mov [rdi + ring_buffer.tail], r9 ; setting the tail to be zero | |
| mov qword [rdi + ring_buffer.limit], RB_CAPACITY ; Reset the limit to RB_CAPACITY | |
| mov r10, RB_CAPACITY ; Limit is now RB_CAPACITY | |
| .tail_ok: | |
| cmp r8, r9 | |
| je .empty | |
| ; Load where the tail is | |
| lea rax, [rdi + ring_buffer.buffer + r9] | |
| cmp r8, r9 | |
| jae .head_after_tail | |
| mov rdx, r10 | |
| sub rdx, r9 | |
| ret | |
| .head_after_tail: | |
| mov rdx, r8 | |
| sub rdx, r9 | |
| ret | |
| .empty: | |
| xor rax, rax | |
| xor rdx, rdx | |
| ret | |
| ; Input: | |
| ; %rdi = &ring_buffer | |
| ; %rsi = length | |
| ; Output: | |
| ; %rax = pointer | |
| ; Clobbers: | |
| ; %r8 | |
| ; %r9 | |
| ; %r10 | |
| ; %r11 | |
| rb_write_span_len: | |
| ; If the length requested is zero, fail. | |
| test rsi, rsi | |
| jz .fail | |
| ; If the length requested is bigger than the capacity, fail. | |
| cmp rsi, RB_CAPACITY - 1 | |
| ja .fail | |
| mov r8, [rdi + ring_buffer.head] | |
| mov r9, [rdi + ring_buffer.tail] | |
| mov r10, [rdi + ring_buffer.limit] | |
| ; if the limit != RB_CAPACITY, we already have an artificial wrap. | |
| ; In that state head must be before tail, only [head ... tail-1] is writable | |
| cmp r10, RB_CAPACITY | |
| jne .artificial_wrap_active | |
| cmp r8, r9 | |
| jb .head_before_tail | |
| .head_after_or_equal_tail: | |
| ; try head ... RB_CAPACITY | |
| mov r11, RB_CAPACITY | |
| sub r11, r8 | |
| ; if tail == 0, reserve one byte | |
| test r9, r9 | |
| jnz .check_end_space | |
| dec r11 | |
| .check_end_space: | |
| cmp r11, rsi | |
| jae .use_current_head ; We can use this. | |
| ; not enough at end, try wrapping to [0...tail-1] | |
| test r9, r9 | |
| jz .fail | |
| mov r11, r9 | |
| dec r11 | |
| cmp r11, rsi | |
| jb .fail ; Not enough space. | |
| ; Abandon [head ... RB_CAPACITY] as padding. | |
| ; Reader will stop at limit=head and then wrap tail to 0 | |
| mov [rdi + ring_buffer.limit], r8 | |
| xor r8, r8 | |
| mov [rdi + ring_buffer.head], r8 | |
| lea rax, [rdi + ring_buffer.buffer] | |
| ret | |
| .head_before_tail: | |
| ; writable span is [head .. tail-1] | |
| mov r11, r9 ; Load tail | |
| sub r11, r8 ; Subtract tail | |
| dec r11 ; Minus one | |
| ; If the data is too little, return failure | |
| cmp r11, rsi | |
| jb .fail | |
| ; Should be good to go | |
| jmp .use_current_head | |
| .artificial_wrap_active: | |
| ; limit < RB_CAPACITY means tail side readable region ends at limit | |
| ; The writer has already wrapped, so only [head ... tail-1] is safe | |
| cmp r8, r9 | |
| jae .fail | |
| mov r11, r9 | |
| sub r11, r8 | |
| dec r11 | |
| cmp r11, rsi | |
| jb .fail | |
| ; Head == %r8 | |
| .use_current_head: | |
| lea rax, [rdi + ring_buffer.buffer + r8] | |
| ret | |
| .fail: | |
| mov rax, -1 | |
| ret | |
| ; Input: | |
| ; %rdi = &ring_buffer | |
| ; %rsi = amount | |
| ; Clobbers: | |
| ; %rax | |
| ; %rcx | |
| rb_advance_tail: | |
| mov rax, [rdi + ring_buffer.tail] | |
| mov rcx, [rdi + ring_buffer.limit] | |
| add rax, rsi ; Add the amount | |
| cmp rax, rcx ; Check if we've gone over. | |
| jb .store ; Good to go | |
| cmp rax, rcx | |
| jne .overflow_advance | |
| xor rax, rax ; zero out the tail. | |
| mov qword [rdi + ring_buffer.limit], RB_CAPACITY | |
| .store: | |
| mov [rdi + ring_buffer.tail], rax | |
| ret | |
| .overflow_advance: | |
| ; consumed crossed limit. Convert excess into new tail position. | |
| ; excess = rax - limit | |
| sub rax, rcx | |
| mov qword [rdi + ring_buffer.limit], RB_CAPACITY | |
| mov [rdi + ring_buffer.tail], rax | |
| ret | |
| ; Input: | |
| ; %rdi = &ring_buffer | |
| ; %rsi = amount | |
| ; Clobbers: | |
| ; %rax | |
| rb_advance_head: | |
| mov rax, [rdi + ring_buffer.head] ; Load the RB's current head | |
| add rax, rsi ; Add the amount | |
| cmp rax, RB_CAPACITY ; Check if we've gone over. | |
| jb .store ; Good to go | |
| sub rax, RB_CAPACITY ; Trim the fat | |
| .store: | |
| mov [rdi + ring_buffer.head], rax | |
| ret |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment