C言語でおすすめのリングバッファのライブラリを教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Verse(UEFN), Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
目次
embedded-resources:おすすめのリングバッファのライブラリ
embedded-resourcesは,C言語でおすすめのリングバッファ(サーキュラーバッファ,環状バッファ)のライブラリです.
リングバッファとは,リング状に配置されたバッファのことです.
バッファを物理的にはリング状にできるわけではないので,インデックス(添字)をバッファサイズで割って剰余をで正規化します.
バッファを一定の範囲に限定し,線形のバッファを擬似的にリングとして扱うことで,リングバッファを実現します.
embedded-resourcesをgitでダウンロードしたい場合は以下のコマンドを実行しましょう.
1 |
$ git clone https://github.com/embeddedartistry/embedded-resources/ |
embedded-resourcesは,他の言語のライブラリ等を含んでいます.
そこで,本記事では,embedded-resourcesでC言語のリングバッファのライブラリ部分だけを抽出して,すぐに使える状態で提供します.
embedded-resourcesのC言語のリングバッファのコード
embedded-resourcesのC言語のリングバッファのコードは以下になります.
- circular_buffer.h
- circular_buffer.c
- circular_buffer_test.c
これらのファイルのコードを整形して読みやすく改良したものを紹介していきます.
また,これらのファイルはこちらからダウンロードできます.
unzipコマンドでcircular_buffer.zipファイルを解凍します.
1 |
$ unzip circular_buffer.zip |
circular_buffer.h
circular_buffer.hは,リングバッファの定義があります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 |
#ifndef CIRCULAR_BUFFER_H_ #define CIRCULAR_BUFFER_H_ #include <stdbool.h> /// Opaque circular buffer structure typedef struct circular_buf_t circular_buf_t; /// Handle type, the way users interact with the API typedef circular_buf_t *cbuf_handle_t; /// Pass in a storage buffer and size, returns a circular buffer handle /// Requires: buffer is not NULL, size > 0 /// Ensures: cbuf has been created and is returned in an empty state cbuf_handle_t circular_buf_init(uint8_t *buffer, size_t size); /// Free a circular buffer structure /// Requires: cbuf is valid and created by circular_buf_init /// Does not free data buffer; owner is responsible for that void circular_buf_free(cbuf_handle_t cbuf); /// Reset the circular buffer to empty, head == tail. Data not cleared /// Requires: cbuf is valid and created by circular_buf_init void circular_buf_reset(cbuf_handle_t cbuf); /// Put version 1 continues to add data if the buffer is full /// Old data is overwritten /// Requires: cbuf is valid and created by circular_buf_init void circular_buf_put(cbuf_handle_t cbuf, uint8_t data); /// Put Version 2 rejects new data if the buffer is full /// Requires: cbuf is valid and created by circular_buf_init /// Returns 0 on success, -1 if buffer is full int circular_buf_put2(cbuf_handle_t cbuf, uint8_t data); /// Retrieve a value from the buffer /// Requires: cbuf is valid and created by circular_buf_init /// Returns 0 on success, -1 if the buffer is empty int circular_buf_get(cbuf_handle_t cbuf, uint8_t *data); /// CHecks if the buffer is empty /// Requires: cbuf is valid and created by circular_buf_init /// Returns true if the buffer is empty bool circular_buf_empty(cbuf_handle_t cbuf); /// Checks if the buffer is full /// Requires: cbuf is valid and created by circular_buf_init /// Returns true if the buffer is full bool circular_buf_full(cbuf_handle_t cbuf); /// Check the capacity of the buffer /// Requires: cbuf is valid and created by circular_buf_init /// Returns the maximum capacity of the buffer size_t circular_buf_capacity(cbuf_handle_t cbuf); /// Check the number of elements stored in the buffer /// Requires: cbuf is valid and created by circular_buf_init /// Returns the current number of elements in the buffer size_t circular_buf_size(cbuf_handle_t cbuf); //TODO: int circular_buf_get_range(circular_buf_t cbuf, uint8_t *data, size_t len); //TODO: int circular_buf_put_range(circular_buf_t cbuf, uint8_t * data, size_t len); #endif //CIRCULAR_BUFFER_H_ |
circuler_buffer.c
circualr_buffer.cは,リングバッファのライブラリです.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 |
#include <stdlib.h> #include <stdint.h> #include <stddef.h> #include <assert.h> #include "circular_buffer.h" // The definition of our circular buffer structure is hidden from the user struct circular_buf_t { uint8_t *buffer; size_t head; size_t tail; size_t max; //of the buffer bool full; }; static void advance_pointer(cbuf_handle_t cbuf) { assert(cbuf); if (circular_buf_full(cbuf)) { cbuf->tail = (cbuf->tail + 1) % cbuf->max; } cbuf->head = (cbuf->head + 1) % cbuf->max; // We mark full because we will advance tail on the next time around cbuf->full = (cbuf->head == cbuf->tail); } static void retreat_pointer(cbuf_handle_t cbuf) { assert(cbuf); cbuf->full = false; cbuf->tail = (cbuf->tail + 1) % cbuf->max; } cbuf_handle_t circular_buf_init(uint8_t *buffer, size_t size) { assert(buffer && size); cbuf_handle_t cbuf = malloc(sizeof(circular_buf_t)); assert(cbuf); cbuf->buffer = buffer; cbuf->max = size; circular_buf_reset(cbuf); assert(circular_buf_empty(cbuf)); return cbuf; } void circular_buf_free(cbuf_handle_t cbuf) { assert(cbuf); free(cbuf); } void circular_buf_reset(cbuf_handle_t cbuf) { assert(cbuf); cbuf->head = 0; cbuf->tail = 0; cbuf->full = false; } size_t circular_buf_size(cbuf_handle_t cbuf) { assert(cbuf); size_t size = cbuf->max; if (!circular_buf_full(cbuf)) { if (cbuf->head >= cbuf->tail) { size = (cbuf->head - cbuf->tail); } else { size = (cbuf->max + cbuf->head - cbuf->tail); } } return size; } size_t circular_buf_capacity(cbuf_handle_t cbuf) { assert(cbuf); return cbuf->max; } void circular_buf_put(cbuf_handle_t cbuf, uint8_t data) { assert(cbuf && cbuf->buffer); cbuf->buffer[cbuf->head] = data; advance_pointer(cbuf); } int circular_buf_put2(cbuf_handle_t cbuf, uint8_t data) { int r = -1; assert(cbuf && cbuf->buffer); if (!circular_buf_full(cbuf)) { cbuf->buffer[cbuf->head] = data; advance_pointer(cbuf); r = 0; } return r; } int circular_buf_get(cbuf_handle_t cbuf, uint8_t *data) { assert(cbuf && data && cbuf->buffer); int r = -1; if (!circular_buf_empty(cbuf)) { *data = cbuf->buffer[cbuf->tail]; retreat_pointer(cbuf); r = 0; } return r; } bool circular_buf_empty(cbuf_handle_t cbuf) { assert(cbuf); return (!circular_buf_full(cbuf) && (cbuf->head == cbuf->tail)); } bool circular_buf_full(cbuf_handle_t cbuf) { assert(cbuf); return cbuf->full; } |
circuler_buffer_test.c
circular_buffer_test.cは,リングバッファのライブラリを利用するためのファイルです.
8行目の"circular_buffer/circular_buffer.h"を"circular_buffer.h"に変更した点以外は同じコードです.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 |
#include <stdio.h> #include <stdlib.h> #include <stdint.h> #include <stddef.h> #include <stdbool.h> #include <assert.h> #include "circular_buffer.h" #define EXAMPLE_BUFFER_SIZE 10 void print_buffer_status(cbuf_handle_t cbuf); int main(void) { uint8_t *buffer = malloc(EXAMPLE_BUFFER_SIZE * sizeof(uint8_t)); printf("\n=== C Circular Buffer Check ===\n"); cbuf_handle_t cbuf = circular_buf_init(buffer, EXAMPLE_BUFFER_SIZE); printf("Buffer initialized. "); print_buffer_status(cbuf); printf("\n******\nAdding %d values\n", EXAMPLE_BUFFER_SIZE - 1); for (uint8_t i = 0; i < (EXAMPLE_BUFFER_SIZE - 1); i++) { circular_buf_put(cbuf, i); printf("Added %u, Size now: %zu\n", i, circular_buf_size(cbuf)); } print_buffer_status(cbuf); printf("\n******\nAdding %d values\n", EXAMPLE_BUFFER_SIZE); for (uint8_t i = 0; i < EXAMPLE_BUFFER_SIZE; i++) { circular_buf_put(cbuf, i); printf("Added %u, Size now: %zu\n", i, circular_buf_size(cbuf)); } print_buffer_status(cbuf); printf("\n******\nReading back values: "); while (!circular_buf_empty(cbuf)) { uint8_t data; circular_buf_get(cbuf, &data); printf("%u ", data); } printf("\n"); print_buffer_status(cbuf); printf("\n******\nAdding %d values\n", EXAMPLE_BUFFER_SIZE + 5); for (uint8_t i = 0; i < EXAMPLE_BUFFER_SIZE + 5; i++) { circular_buf_put(cbuf, i); printf("Added %u, Size now: %zu\n", i, circular_buf_size(cbuf)); } print_buffer_status(cbuf); printf("\n******\nReading back values: "); while (!circular_buf_empty(cbuf)) { uint8_t data; circular_buf_get(cbuf, &data); printf("%u ", data); } printf("\n"); printf("\n******\nAdding %d values using non-overwrite version\n", EXAMPLE_BUFFER_SIZE + 5); for (uint8_t i = 0; i < EXAMPLE_BUFFER_SIZE + 5; i++) { circular_buf_put2(cbuf, i); } print_buffer_status(cbuf); printf("\n******\nReading back values: "); while (!circular_buf_empty(cbuf)) { uint8_t data; circular_buf_get(cbuf, &data); printf("%u ", data); } printf("\n"); free(buffer); circular_buf_free(cbuf); return 0; } void print_buffer_status(cbuf_handle_t cbuf) { printf("Full: %d, empty: %d, size: %zu\n", circular_buf_full(cbuf), circular_buf_empty(cbuf), circular_buf_size(cbuf)); } |
Makefile
リングバッファ用のファイルをビルドして実行するために,私が作成したMakefileです.
Makefileと同じディレクトリにcircular_buffer.h,circular_buffer.c,circular_buffer_test.cがあることを想定しています.
circular_buffer.cとcircular_buffer_test.cファイルをビルドして実行ファイルcircular_bufferを生成します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# # Circular Buffer # # Author: Hiroyuki Chishiro # SRCS = circular_buffer.c circular_buffer_test.c OBJS = $(SRCS:.c=.o) TARGET = circular_buffer CC = gcc RM = rm -f CFLAGS += -Wall .PHONY: all clean all: $(TARGET) $(TARGET): $(OBJS) $(CC) -o $@ $(OBJS) $(CFLAGS) $(LDFLAGS) clean: $(RM) -r $(TARGET) $(OBJS) *~ |
circular_buffer.zipファイルを解凍した時に作成したcirculr_bufferディレクトリに移動し,makeでビルドして実行すると,以下のようになります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 |
$ cd circular_buffer $ make gcc -Wall -c -o circular_buffer.o circular_buffer.c gcc -Wall -c -o circular_buffer_test.o circular_buffer_test.c gcc -o circular_buffer circular_buffer.o circular_buffer_test.o -Wall $ circular_buffer === C Circular Buffer Check === Buffer initialized. Full: 0, empty: 1, size: 0 ****** Adding 9 values Added 0, Size now: 1 Added 1, Size now: 2 Added 2, Size now: 3 Added 3, Size now: 4 Added 4, Size now: 5 Added 5, Size now: 6 Added 6, Size now: 7 Added 7, Size now: 8 Added 8, Size now: 9 Full: 0, empty: 0, size: 9 ****** Adding 10 values Added 0, Size now: 10 Added 1, Size now: 10 Added 2, Size now: 10 Added 3, Size now: 10 Added 4, Size now: 10 Added 5, Size now: 10 Added 6, Size now: 10 Added 7, Size now: 10 Added 8, Size now: 10 Added 9, Size now: 10 Full: 1, empty: 0, size: 10 ****** Reading back values: 0 1 2 3 4 5 6 7 8 9 Full: 0, empty: 1, size: 0 ****** Adding 15 values Added 0, Size now: 1 Added 1, Size now: 2 Added 2, Size now: 3 Added 3, Size now: 4 Added 4, Size now: 5 Added 5, Size now: 6 Added 6, Size now: 7 Added 7, Size now: 8 Added 8, Size now: 9 Added 9, Size now: 10 Added 10, Size now: 10 Added 11, Size now: 10 Added 12, Size now: 10 Added 13, Size now: 10 Added 14, Size now: 10 Full: 1, empty: 0, size: 10 ****** Reading back values: 5 6 7 8 9 10 11 12 13 14 ****** Adding 15 values using non-overwrite version Full: 1, empty: 0, size: 10 ****** Reading back values: 0 1 2 3 4 5 6 7 8 9 |
まとめ
C言語でおすすめのリングバッファのライブラリ「embedded-resources」を紹介しました.
embedded-resourcesのリングバッファのファイルと行数をまとめると以下になります.
- circular_buffer.h:64行
- circuler_buffer.c:147行
- circuler_buffer_test.c:105行
合計317行と短いだけでなく,コードもシンプルで読みやすいので,C言語でリングバッファを利用する場合はembedded-resourcesをおすすめします.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!