diff --git a/singe/common.h b/singe/common.h new file mode 100644 index 000000000..0b9b1228c --- /dev/null +++ b/singe/common.h @@ -0,0 +1,34 @@ +/* + * + * Singe 2 + * Copyright (C) 2019 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef COMMON_H +#define COMMON_H + + +#define byte unsigned char + +#define bool unsigned char +#define true 1 +#define false 0 + + +#endif // COMMON_H diff --git a/singe/main.c b/singe/main.c index 576f77cf3..65e0fb90a 100644 --- a/singe/main.c +++ b/singe/main.c @@ -20,319 +20,69 @@ */ -/* - * NOTES: - * - * - What if the video source audio isn't in stereo? - * - */ - - -#include - #include #include #include #include -#include #include -#include "thirdparty/utarray.h" -#include "thirdparty/c-ringbuf/ringbuf.h" - #include "stddclmr.h" +#include "common.h" +#include "util.h" +#include "videoPlayer.h" // /home/scott/code/singe2/videotest/Stargate.m4v // /home/scott/code/singe2/videotest/TestClip_720x480_29.97fps_2000_frames.avi -#define byte unsigned char - -#define bool unsigned char -#define true 1 -#define false 0 - -#define AUDIO_RING_SIZE (64 * 1024) -#define AUDIO_SAMPLES (AUDIO_RING_SIZE / (16 /* average bytes per sample */ * 2 /* channels */)) -#define AUDIO_SILENCE_SECONDS 2 -#define AUDIO_CHUNK_MULTIPLIER 2 - - -typedef struct AudioRingBufferS { - ringbuf_t ring; - size_t sizeTemp; - SDL_mutex *mutex; -} AudioRingBufferT; - - -void dequeueVideoAudio(int channel, void *stream, int len, void *udata); -void die(char *fmt, ...); -int FFMS_CC indexCallBack(int64_t Current, int64_t Total, void *ICPrivate); -void say(char *fmt, ...); - - -void dequeueVideoAudio(int channel, void *stream, int len, void *udata) { - int toCopy = len; - AudioRingBufferT *ring = (AudioRingBufferT *)udata; - - (void)channel; - - ring->sizeTemp = ringbuf_bytes_used(ring->ring); // We do this in case more space is used by the mixer while we're working on this - if (len > (int)ring->sizeTemp) { - toCopy = (int)ring->sizeTemp; - } - if (SDL_LockMutex(ring->mutex) < 0) die("Unable to lock mutex for ringbuf_memcpy_from"); - ringbuf_memcpy_from(stream, ring->ring, (size_t)toCopy); - if (SDL_UnlockMutex(ring->mutex) < 0) die("Unable to unlock mutex for ringbuf_memcpy_from"); - //say("Dequeued %d bytes of audio data. Buffer is now %d.", toCopy, ringbuf_bytes_used(ring->ring)); -} - - -__attribute__((__format__(__printf__, 1, 0))) -void die(char *fmt, ...) { - va_list args; - va_start(args, fmt); - vfprintf(stderr, fmt, args); - va_end(args); - printf("\n"); - fflush(stderr); - exit(1); -} - - -int FFMS_CC indexCallBack(int64_t current, int64_t total, void *ICPrivate) { - static int lastPercent = 0; - int thisPercent = 0; - - (void)ICPrivate; - - if ((current == 0) && (total == 0)) { - lastPercent = 0; // Reset - } else { - thisPercent = (int)((double)current / (double)total * 100.0); - if (thisPercent != lastPercent) { - lastPercent = thisPercent; - say("Indexing: %d%%", thisPercent); - } - } - - return 0; -} - - -__attribute__((__format__(__printf__, 1, 0))) -void say(char *fmt, ...) { - va_list args; - va_start(args, fmt); - vfprintf(stdout, fmt, args); - va_end(args); - printf("\n"); - fflush(stdout); -} - - int main(int argc, char *argv[]) { int err = 0; int flags = 0; - int audioTrack = -1; - int videoTrack = -1; - int pixelFormats[2]; - int frame = 0; - int audioSampleSize = 0; - int audioSilenceChannel = -1; - bool running = true; - bool playing = true; - bool resetTime = false; - int64_t frameDeltaTime = 0; - int64_t lastFrameTime = 0; - int64_t timestamp = 0; - int64_t audioPosition = 0; - int64_t audioCount = 0; - Uint16 audioFormat = 0; - Uint32 lastTickTime = 0; - Uint32 audioSilenceSize = 0; + int thisFrame = -1; + int lastFrame = -1; + int videoHandle = -1; char *filename = argv[1]; - char errMsg[1024]; - char indexName[1024]; - byte *audioBuffer = NULL; - byte audioSampleBytes = 0; - byte *audioSilenceRaw = NULL; - AudioRingBufferT audioRingBuffer; + bool running = true; SDL_Window *window = NULL; SDL_Renderer *renderer = NULL; - Mix_Chunk *silenceChunk = NULL; - FFMS_Index *index = NULL; - FFMS_Indexer *indexer = NULL; - SDL_Texture *videoTexture = NULL; - FFMS_ErrorInfo errInfo; - FFMS_AudioSource *audioSource = NULL; - FFMS_VideoSource *videoSource = NULL; - const FFMS_AudioProperties *audioProps = NULL; - const FFMS_VideoProperties *videoProps = NULL; - const FFMS_Frame *propFrame = NULL; - const FFMS_TrackTimeBase *videoTimeBase = NULL; - const FFMS_Frame *frameData = NULL; - const FFMS_FrameInfo *frameInfo = NULL; + SDL_Texture *texture = NULL; // Did we get a filename to open? - if (argc != 2) die("Usage: %s \n", argv[0]); + if (argc != 2) utilDie("Usage: %s \n", argv[0]); // Init SDL err = SDL_Init(SDL_INIT_EVERYTHING); - if (err != 0) die("%s", SDL_GetError()); + if (err != 0) utilDie("%s", SDL_GetError()); // Init SDL_mixer flags = MIX_INIT_FLAC | MIX_INIT_MOD | MIX_INIT_MP3 | MIX_INIT_OGG | MIX_INIT_MID | MIX_INIT_OPUS; err = Mix_Init(flags); - if (err != flags) die("%s", Mix_GetError()); + if (err != flags) utilDie("%s", Mix_GetError()); // Create Resizable Window window = SDL_CreateWindow(filename, SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_RESIZABLE); - if (window == NULL) die("%s", SDL_GetError()); + if (window == NULL) utilDie("%s", SDL_GetError()); // Create an accelerated renderer. renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED); - if (renderer == NULL) die("%s", SDL_GetError()); + if (renderer == NULL) utilDie("%s", SDL_GetError()); // Clear screen with black SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255); SDL_RenderClear(renderer); - // Start FFMS - FFMS_Init(0, 0); - errInfo.Buffer = errMsg; - errInfo.BufferSize = sizeof(errMsg); - errInfo.ErrorType = FFMS_ERROR_SUCCESS; - errInfo.SubType = FFMS_ERROR_SUCCESS; - - // Index file - snprintf(indexName, 1024, "%s.index", filename); - index = FFMS_ReadIndex(indexName, &errInfo); - if (index) { - if (FFMS_IndexBelongsToFile(index, filename, &errInfo)) { - FFMS_DestroyIndex(index); - index = NULL; - say("Cached index is invalid."); - } else { - say("Loaded cached index."); - } - } - if (!index) { - say("Creating new index."); - indexer = FFMS_CreateIndexer(filename, &errInfo); - if (indexer == NULL) die("%s", errInfo.Buffer); - FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_AUDIO, 1, 0); - FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_VIDEO, 1, 0); - indexCallBack(0, 0, NULL); - FFMS_SetProgressCallback(indexer, indexCallBack, NULL); - index = FFMS_DoIndexing2(indexer, FFMS_IEH_ABORT, &errInfo); - if (index == NULL) die("%s", errInfo.Buffer); - if (FFMS_WriteIndex(indexName, index, &errInfo)) die("%s", errInfo.Buffer); - } - - // Find video track - videoTrack = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_VIDEO, &errInfo); - if (videoTrack < 0) die("%s", errInfo.Buffer); - videoSource = FFMS_CreateVideoSource(filename, videoTrack, index, 1, FFMS_SEEK_NORMAL, &errInfo); - if (videoSource == NULL) die("%s", errInfo.Buffer); - - // Get video properties - videoProps = FFMS_GetVideoProperties(videoSource); - propFrame = FFMS_GetFrame(videoSource, 0, &errInfo); - if (propFrame == NULL) die("%s", errInfo.Buffer); - videoTimeBase = FFMS_GetTimeBase(FFMS_GetTrackFromVideo(videoSource)); - - // Set up output format - pixelFormats[0] = FFMS_GetPixFmt("bgra"); - pixelFormats[1] = -1; - if (FFMS_SetOutputFormatV2(videoSource, pixelFormats, propFrame->EncodedWidth, propFrame->EncodedHeight, FFMS_RESIZER_BICUBIC, &errInfo)) die("%s", errInfo.Buffer); - - // Find audio track - audioTrack = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_AUDIO, &errInfo); - if (audioTrack < 0) die("%s", errInfo.Buffer); - audioSource = FFMS_CreateAudioSource(filename, audioTrack, index, FFMS_DELAY_FIRST_VIDEO_TRACK, &errInfo); - if (audioSource == NULL) die("%s", errInfo.Buffer); - - // Get audio properties - audioProps = FFMS_GetAudioProperties(audioSource); - - // Index is now part of audioSource & videoSource, so release this one - FFMS_DestroyIndex(index); - - // Create video texture - SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); - videoTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STATIC, propFrame->EncodedWidth, propFrame->EncodedHeight); - if (videoTexture == NULL) die("%s", SDL_GetError()); - - SDL_RenderSetLogicalSize(renderer, propFrame->EncodedWidth, propFrame->EncodedHeight); - - // Determine audio format - switch (audioProps->SampleFormat) { - case FFMS_FMT_U8: - audioFormat = AUDIO_U8; - audioSampleBytes = 1; - break; - case FFMS_FMT_S16: - audioFormat = AUDIO_S16SYS; - audioSampleBytes = 2; - break; - case FFMS_FMT_S32: - audioFormat = AUDIO_S32SYS; - audioSampleBytes = 4; - break; - case FFMS_FMT_FLT: - audioFormat = AUDIO_F32SYS; - audioSampleBytes = 4; - break; - default: - die("Unknown audio sample format."); - break; - } - if (audioProps->Channels > 2) die("Only mono and stereo audio are supported."); - // Create audio mixer device - err = Mix_OpenAudio(audioProps->SampleRate, audioFormat, 2, audioSampleSize * audioProps->Channels * AUDIO_CHUNK_MULTIPLIER); - if (err != 0) die("%s", Mix_GetError()); + err = Mix_OpenAudio(44100, MIX_DEFAULT_FORMAT, 2, 44100 /* freq */ * 16 /* bits */ * 2 /* channels */ * 2 /* seconds */); + if (err != 0) utilDie("%s", Mix_GetError()); - // Create a buffer for reading audio from the video stream - audioSampleSize = audioSampleBytes * audioProps->Channels; - audioBuffer = (byte *)malloc((size_t)(audioSampleSize * AUDIO_SAMPLES) * sizeof(byte)); - if (!audioBuffer) die("Unable to allocate %ld byte audio buffer.", ((size_t)(audioSampleSize * AUDIO_SAMPLES) * sizeof(byte))); + if (videoInit()) utilDie("Unable to initialize video player."); - // Create a ring buffer to pass audio data from the video to the mixer - audioRingBuffer.ring = ringbuf_new(AUDIO_RING_SIZE); - if (!audioRingBuffer.ring) die("Unable to allocate %ld audio ring buffer.", AUDIO_RING_SIZE); - audioRingBuffer.mutex = SDL_CreateMutex(); - if (!audioRingBuffer.mutex) die("Unable to create audio ring buffer mutex."); + videoHandle = videoLoad(filename, renderer); + if (videoHandle < 0) utilDie("Unable to load video file: %s", filename); - // Create a block of silent audio to overlay with video stream audio - audioSilenceSize = (Uint32)audioSampleSize * (Uint32)audioProps->SampleRate * AUDIO_SILENCE_SECONDS; - audioSilenceRaw = (byte *)calloc(1, (size_t)audioSilenceSize * sizeof(byte)); - if (!audioSilenceRaw) die("Unable to allocate %ld silence buffer.", audioSilenceSize); - - // Load silent audio - silenceChunk = Mix_QuickLoad_RAW(audioSilenceRaw, audioSilenceSize); - if (!silenceChunk) die("%s", Mix_GetError()); - - // Start silent audio playback & immediately pause it - audioSilenceChannel = Mix_PlayChannel(-1, silenceChunk, -1); - if (audioSilenceChannel < 0) die("%s", Mix_GetError()); - - // Register effect to provide video stream audio on this channel - Mix_RegisterEffect(audioSilenceChannel, dequeueVideoAudio, NULL, &audioRingBuffer); - - // Play It - say("Frames: %d (%dx%d) Audio Samples: %ld (%d Hz) %d Channel%s", - videoProps->NumFrames, - propFrame->EncodedWidth, - propFrame->EncodedHeight, - audioProps->NumSamples, - audioProps->SampleRate, - audioProps->Channels, - audioProps->Channels == 1 ? "" : "s" - ); + videoPlay(videoHandle); while (running) { @@ -345,35 +95,25 @@ int main(int argc, char *argv[]) { running = false; break; case SDLK_SPACE: - playing = !playing; - resetTime = playing; + if (videoIsPlaying(videoHandle)) { + videoPause(videoHandle); + } else { + videoPlay(videoHandle); + } break; case SDLK_RIGHT: - frame++; - resetTime = true; + videoSeek(videoHandle, lastFrame + 1); break; case SDLK_LEFT: - frame--; - resetTime = true; + videoSeek(videoHandle, lastFrame - 1); break; case SDLK_UP: - frame += 100; - resetTime = true; + videoSeek(videoHandle, lastFrame + 100); break; case SDLK_DOWN: - frame -= 100; - resetTime = true; + videoSeek(videoHandle, lastFrame - 100); break; } - if (resetTime) { - if (frame >= videoProps->NumFrames) { - frame = 0; - } - if (frame < 0) { - frame = videoProps->NumFrames - 1; - } - say("Seeking to frame %d", frame); - } break; case SDL_QUIT: @@ -382,83 +122,22 @@ int main(int argc, char *argv[]) { } } - // Handle video frames (and time) - if ((SDL_GetTicks() - lastTickTime >= frameDeltaTime) || resetTime) { - lastTickTime = SDL_GetTicks(); - - if (frameData) { - SDL_UpdateTexture(videoTexture, NULL, frameData->Data[0], frameData->Linesize[0]); - SDL_RenderCopy(renderer, videoTexture, NULL, NULL); - SDL_RenderPresent(renderer); - } - - frameData = FFMS_GetFrame(videoSource, frame, &errInfo); - if (frameData == NULL) die("%s", errInfo.Buffer); - frameInfo = FFMS_GetFrameInfo(FFMS_GetTrackFromVideo(videoSource), frame); - timestamp = (int64_t)((frameInfo->PTS * videoTimeBase->Num) / (double)videoTimeBase->Den); // Convert to milliseconds - frameDeltaTime = timestamp - lastFrameTime; - lastFrameTime = timestamp; - - if (playing) { - if (++frame >= videoProps->NumFrames) { - frame = 0; - timestamp = 0; - resetTime = true; - } - } - - if (resetTime) { - if (SDL_LockMutex(audioRingBuffer.mutex) < 0) die("Unable to lock mutex for ringbuf_reset"); - ringbuf_reset(audioRingBuffer.ring); - if (SDL_UnlockMutex(audioRingBuffer.mutex) < 0) die("Unable to unlock mutex for ringbuf_reset"); - lastTickTime = 0; - frameDeltaTime = 0; - audioPosition = (int64_t)((double)(timestamp * 0.001) * (double)audioProps->SampleRate); - resetTime = false; - } - } - - // Handle audio samples - if ((playing) && (!ringbuf_is_full(audioRingBuffer.ring)) && (audioPosition < audioProps->NumSamples)) { - // Maximum samples we can read at a time - audioCount = AUDIO_SAMPLES; - // Don't read past end of audio data - if (audioPosition + audioCount >= audioProps->NumSamples) { - audioCount = audioProps->NumSamples - audioPosition - 1; - } - // Will this fit in our ring buffer? - audioRingBuffer.sizeTemp = ringbuf_bytes_free(audioRingBuffer.ring); // We do this in case more space is freed by the mixer while we're working on this - if ((audioRingBuffer.sizeTemp) < (size_t)(audioCount * audioSampleSize)) { - audioCount = (int64_t)(audioRingBuffer.sizeTemp / (size_t)audioSampleSize); - } - // Are we reading anything? - if (audioCount > 0) { - if (FFMS_GetAudio(audioSource, audioBuffer, audioPosition, audioCount, &errInfo)) die("%s", errInfo.Buffer); - if (SDL_LockMutex(audioRingBuffer.mutex) < 0) die("Unable to lock mutex for ringbuf_memcpy_into"); - ringbuf_memcpy_into(audioRingBuffer.ring, audioBuffer, (size_t)(audioCount * audioSampleSize)); - if (SDL_UnlockMutex(audioRingBuffer.mutex) < 0) die("Unable to unlock mutex for ringbuf_memcpy_into"); - //say("Added %d samples (%d bytes) to ring buffer. Buffer is now %d bytes", audioCount, audioCount * audioSampleSize, ringbuf_bytes_used(audioRingBuffer.ring)); - audioPosition += audioCount; - } + thisFrame = videoUpdate(videoHandle, &texture); + if ((thisFrame != lastFrame) && (thisFrame >= 0)) { + utilSay("Presenting frame %d", thisFrame); + lastFrame = thisFrame; + SDL_RenderCopy(renderer, texture, NULL, NULL); + SDL_RenderPresent(renderer); } } + videoUnload(videoHandle); + + videoQuit(); // Shutdown - Mix_HaltChannel(audioSilenceChannel); - Mix_UnregisterEffect(audioSilenceChannel, dequeueVideoAudio); - Mix_FreeChunk(silenceChunk); - free(audioBuffer); - free(audioSilenceRaw); - ringbuf_free(&audioRingBuffer.ring); - SDL_DestroyMutex(audioRingBuffer.mutex); + Mix_CloseAudio(); - FFMS_DestroyAudioSource(audioSource); - FFMS_DestroyVideoSource(videoSource); - FFMS_Deinit(); - - //SDL_CloseAudioDevice(audioDevice); - SDL_DestroyTexture(videoTexture); SDL_DestroyRenderer(renderer); SDL_DestroyWindow(window); Mix_Quit(); diff --git a/singe/singe.pro b/singe/singe.pro index fa139bb84..26504f74d 100644 --- a/singe/singe.pro +++ b/singe/singe.pro @@ -90,20 +90,23 @@ MANYMOUSE_SOURCES = \ # === SINGE === -INCLUDEPATH += \ - $$MANYMOUSE_INCLUDES \ - $$PWD/../thirdparty-build/$$BITNESS/installed/include +QMAKE_CFLAGS += \ + -isystem $$MANYMOUSE_INCLUDES \ + -isystem $$PWD/../thirdparty-build/$$BITNESS/installed/include HEADERS += \ $$MANYMOUSE_HEADERS \ stddclmr.h \ - thirdparty/utarray.h \ - thirdparty/c-ringbuf/ringbuf.h + thirdparty/uthash.h \ + common.h \ + util.h \ + videoPlayer.h SOURCES += \ $$MANYMOUSE_SOURCES \ - main.c \ - thirdparty/c-ringbuf/ringbuf.c + util.c \ + videoPlayer.c \ + main.c LIBS += \ -L$$PWD/../thirdparty-build/$$BITNESS/installed/lib \ @@ -139,7 +142,8 @@ LIBS += \ OTHER_FILES += \ preBuild.sh \ - postLink.sh + postLink.sh \ + old.c # Strip & UPX the final result #linux:QMAKE_POST_LINK += bash $$PWD/postLink.sh "$$PWD" "$$DESTDIR" diff --git a/singe/thirdparty/c-ringbuf/.github/CONTRIBUTING.md b/singe/thirdparty/c-ringbuf/.github/CONTRIBUTING.md deleted file mode 100644 index c02530131..000000000 --- a/singe/thirdparty/c-ringbuf/.github/CONTRIBUTING.md +++ /dev/null @@ -1,69 +0,0 @@ -# CONTRIBUTING - -For the sake of saving both your time and mine, please read the following guidelines before making a contribution to this project. - -## OPENING ISSUES - -When opening an issue, please first check for an existing issue in the project (in **both** the closed and open issues) that may cover the issue you're experiencing. If an issue is closed and you think it should not be, please feel free to add a new comment in a closed issue thread; I will be notified, and I'll reconsider whether the issue should be re-opened based on your comment. - -If you determine that you need to open a new issue, please include the following details: - - * Your operating system (e.g., Ubuntu 18.04). - * Your system's CPU architecture (e.g., x86-64). - * Your C compiler's version, as shown by, e.g., `clang -v`. - * What steps are necessary to reproduce the issue. - -## PULL REQUESTS - -If you would like to make a pull request (PR) or other contribution to this project, please first refer to the following guidelines. - -### One PR per feature - -Please make one PR/patch per feature. Do not lump multiple features into a single PR. One PR per feature is a hassle for contributors, and for that I am sorry; but it makes reviewing contributions easier, and it also means that, if your contribution creates a bug or other issue, it's easier to track down the root cause. This policy also increases the likelihood that your PRs will be accepted in the case where there is an issue with one part of your PR, but the rest is fine. - -If you are in doubt about what constitutes "a feature," please contact me before making a pull request so that we can sort it out. Feel free to do this by opening an issue that describes what you're proposing to do. - -### Testing - -Make sure you have run all tests successfully before submitting your PR. If you are adding functionality, please add new tests to the test suite to exercise that functionality. If your PR fixes a bug, please write a test(s) that demonstrates: - - 1. how to trigger the bug on the existing code base, and - 2. that once your code has been applied, the bug is fixed. - -### Bug fixes - -If your PR is a bug fix, please first submit an issue that describes the bug in detail and how it occurs (see [OPENING ISSUES](#opening-issues)), and refer to this issue when submitting the PR. - -### Public domain - -By submitting a PR to this project, you are agreeing that your contribution is dedicated to the public domain per the -[COPYING](../COPYING) file included in this distribution, and that you are waiving any copyright claims with respect to your contribution. You also assert that this contribution is your own creation, and not taken from another work. - -### Commit messages - -A commit message should consist of at least a single line describing the changes made by the commit. This line should not be too many characters; I'm not a stickler about line length, but please try to stay below 80 characters or so. - -If one line cannot capture what you want to say about the commit, please feel free to add more detail in subsequent paragraphs. In general, I prefer commit messages that are more detailed than not. Commit messages with details make it possible to understand the motivation for the change you've made. - -([Here](https://github.com/dhess/c-ringbuf/commit/21669475d7f4e13801f94f5031dbd9aa00e95796) is an example of one of my own commits where the commit message adds some important detail motivating why the change was made, and what impacts it might have on the code.) - -There's no need to go overboard, however. If your commit is simple and straightforward, a simple and straightforward single line description will suffice. - -If your commit is a bug fix, please add the following text somewhere in your commit message, obviously replacing "#13" with the issue number that your commit addresses: - -`Fixes #13` - -I reserve the right to reject PRs purely based on whether the commit message is adequate/accurate. - -### Code formatting - -Please respect the code formatting rules I've used for this project. There are a few hard and fast rules: - - * Spaces, not tabs. - * Indent by 4 spaces. - * Macros in `UPPER_CASE`, using underscores (`_`) as separators. - * Everything else in `lower_case`, using underscores (`_`) as separators. - -For other formatting, I'm less picky, but generally speaking, just look for an example in the existing code base and follow it. (It's quite possible that I myself have been inconsistent in a few places. Feel free to point these cases out to me by opening an issue, if you like.) - -I reserve the right to reject PRs purely based on their formatting. Please do not take it personally it if happens to you; everyone has their own quirky way of formatting code, and mine is no better than anyone else's, but I think it's important to be consistent within a project. diff --git a/singe/thirdparty/c-ringbuf/.github/ISSUE_TEMPLATE.md b/singe/thirdparty/c-ringbuf/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 30e2c5deb..000000000 --- a/singe/thirdparty/c-ringbuf/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,14 +0,0 @@ -# BEFORE OPENING AN ISSUE - -Please read the [issue guidelines](https://github.com/dhess/c-ringbuf/blob/master/.github/CONTRIBUTING.md#opening-issues) - -**Before submitting this issue, please delete everything from this line up, and fill in the template below.** - --------------------------------------------------- - -## Issue description - -### Steps to reproduce - -### Technical details - diff --git a/singe/thirdparty/c-ringbuf/.github/PULL_REQUEST_TEMPLATE.md b/singe/thirdparty/c-ringbuf/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index 5985fc03d..000000000 --- a/singe/thirdparty/c-ringbuf/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,11 +0,0 @@ -# BEFORE MAKING A PULL REQUEST - -Please read the [pull request guidelines](https://github.com/dhess/c-ringbuf/blob/master/.github/CONTRIBUTING.md#pull-requests) - -**Before submitting this PR, please delete everything from this line and above, and check the boxes in the template below.** - --------------------------------------------------- - -By submitting this PR, you agree to the following: - -- [ ] This contribution is dedicated to the public domain per the [COPYING](https://github.com/dhess/c-ringbuf/blob/master/COPYING) file included in this distribution. I am waiving any copyright claims with respect to this contribution, and I assert that this contribution is my own creation, and not taken from another work. diff --git a/singe/thirdparty/c-ringbuf/.gitignore b/singe/thirdparty/c-ringbuf/.gitignore deleted file mode 100644 index 35a041495..000000000 --- a/singe/thirdparty/c-ringbuf/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -.DS_Store -*.o -*~ -ringbuf-test diff --git a/singe/thirdparty/c-ringbuf/COPYING b/singe/thirdparty/c-ringbuf/COPYING deleted file mode 100644 index f7beb1453..000000000 --- a/singe/thirdparty/c-ringbuf/COPYING +++ /dev/null @@ -1,122 +0,0 @@ -Creative Commons Legal Code - -CC0 1.0 Universal - - CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE - LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN - ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS - INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES - REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS - PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM - THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED - HEREUNDER. - -Statement of Purpose - -The laws of most jurisdictions throughout the world automatically confer -exclusive Copyright and Related Rights (defined below) upon the creator -and subsequent owner(s) (each and all, an "owner") of an original work of -authorship and/or a database (each, a "Work"). - -Certain owners wish to permanently relinquish those rights to a Work for -the purpose of contributing to a commons of creative, cultural and -scientific works ("Commons") that the public can reliably and without fear -of later claims of infringement build upon, modify, incorporate in other -works, reuse and redistribute as freely as possible in any form whatsoever -and for any purposes, including without limitation commercial purposes. -These owners may contribute to the Commons to promote the ideal of a free -culture and the further production of creative, cultural and scientific -works, or to gain reputation or greater distribution for their Work in -part through the use and efforts of others. - -For these and/or other purposes and motivations, and without any -expectation of additional consideration or compensation, the person -associating CC0 with a Work (the "Affirmer"), to the extent that he or she -is an owner of Copyright and Related Rights in the Work, voluntarily -elects to apply CC0 to the Work and publicly distribute the Work under its -terms, with knowledge of his or her Copyright and Related Rights in the -Work and the meaning and intended legal effect of CC0 on those rights. - -1. Copyright and Related Rights. A Work made available under CC0 may be -protected by copyright and related or neighboring rights ("Copyright and -Related Rights"). Copyright and Related Rights include, but are not -limited to, the following: - - i. the right to reproduce, adapt, distribute, perform, display, - communicate, and translate a Work; - ii. moral rights retained by the original author(s) and/or performer(s); -iii. publicity and privacy rights pertaining to a person's image or - likeness depicted in a Work; - iv. rights protecting against unfair competition in regards to a Work, - subject to the limitations in paragraph 4(a), below; - v. rights protecting the extraction, dissemination, use and reuse of data - in a Work; - vi. database rights (such as those arising under Directive 96/9/EC of the - European Parliament and of the Council of 11 March 1996 on the legal - protection of databases, and under any national implementation - thereof, including any amended or successor version of such - directive); and -vii. other similar, equivalent or corresponding rights throughout the - world based on applicable law or treaty, and any national - implementations thereof. - -2. Waiver. To the greatest extent permitted by, but not in contravention -of, applicable law, Affirmer hereby overtly, fully, permanently, -irrevocably and unconditionally waives, abandons, and surrenders all of -Affirmer's Copyright and Related Rights and associated claims and causes -of action, whether now known or unknown (including existing as well as -future claims and causes of action), in the Work (i) in all territories -worldwide, (ii) for the maximum duration provided by applicable law or -treaty (including future time extensions), (iii) in any current or future -medium and for any number of copies, and (iv) for any purpose whatsoever, -including without limitation commercial, advertising or promotional -purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each -member of the public at large and to the detriment of Affirmer's heirs and -successors, fully intending that such Waiver shall not be subject to -revocation, rescission, cancellation, termination, or any other legal or -equitable action to disrupt the quiet enjoyment of the Work by the public -as contemplated by Affirmer's express Statement of Purpose. - -3. Public License Fallback. Should any part of the Waiver for any reason -be judged legally invalid or ineffective under applicable law, then the -Waiver shall be preserved to the maximum extent permitted taking into -account Affirmer's express Statement of Purpose. In addition, to the -extent the Waiver is so judged Affirmer hereby grants to each affected -person a royalty-free, non transferable, non sublicensable, non exclusive, -irrevocable and unconditional license to exercise Affirmer's Copyright and -Related Rights in the Work (i) in all territories worldwide, (ii) for the -maximum duration provided by applicable law or treaty (including future -time extensions), (iii) in any current or future medium and for any number -of copies, and (iv) for any purpose whatsoever, including without -limitation commercial, advertising or promotional purposes (the -"License"). The License shall be deemed effective as of the date CC0 was -applied by Affirmer to the Work. Should any part of the License for any -reason be judged legally invalid or ineffective under applicable law, such -partial invalidity or ineffectiveness shall not invalidate the remainder -of the License, and in such case Affirmer hereby affirms that he or she -will not (i) exercise any of his or her remaining Copyright and Related -Rights in the Work or (ii) assert any associated claims and causes of -action with respect to the Work, in either case contrary to Affirmer's -express Statement of Purpose. - -4. Limitations and Disclaimers. - - a. No trademark or patent rights held by Affirmer are waived, abandoned, - surrendered, licensed or otherwise affected by this document. - b. Affirmer offers the Work as-is and makes no representations or - warranties of any kind concerning the Work, express, implied, - statutory or otherwise, including without limitation warranties of - title, merchantability, fitness for a particular purpose, non - infringement, or the absence of latent or other defects, accuracy, or - the present or absence of errors, whether or not discoverable, all to - the greatest extent permissible under applicable law. - c. Affirmer disclaims responsibility for clearing rights of other persons - that may apply to the Work or any use thereof, including without - limitation any person's Copyright and Related Rights in the Work. - Further, Affirmer disclaims responsibility for obtaining any necessary - consents, permissions or other rights required for any use of the - Work. - d. Affirmer understands and acknowledges that Creative Commons is not a - party to this document and has no duty or obligation with respect to - this CC0 or use of the Work. - \ No newline at end of file diff --git a/singe/thirdparty/c-ringbuf/Makefile b/singe/thirdparty/c-ringbuf/Makefile deleted file mode 100644 index 4935c45e7..000000000 --- a/singe/thirdparty/c-ringbuf/Makefile +++ /dev/null @@ -1,51 +0,0 @@ -CC=clang -CFLAGS=-O0 -g -Wall -Wpointer-arith -ftrapv -fsanitize=undefined-trap -fsanitize-undefined-trap-on-error - -# or, for gcc... -#CC=gcc -#CFLAGS=-O0 -g -Wall - -LD=$(CC) -LDFLAGS=-g - -test: ringbuf-test - ./ringbuf-test - -coverage: ringbuf-test-gcov - ./ringbuf-test-gcov - gcov -o ringbuf-gcov.o ringbuf.c - -valgrind: ringbuf-test - valgrind ./ringbuf-test - -help: - @echo "Targets:" - @echo - @echo "test - build and run ringbuf unit tests." - @echo "coverage - use gcov to check test coverage of ringbuf.c." - @echo "valgrind - use valgrind to check for memory leaks." - @echo "clean - remove all targets." - @echo "help - this message." - -ringbuf-test-gcov: ringbuf-test-gcov.o ringbuf-gcov.o - gcc -o ringbuf-test-gcov --coverage $^ - -ringbuf-test-gcov.o: ringbuf-test.c ringbuf.h - gcc -c $< -o $@ - -ringbuf-gcov.o: ringbuf.c ringbuf.h - gcc --coverage -c $< -o $@ - -ringbuf-test: ringbuf-test.o ringbuf.o - $(LD) -o ringbuf-test $(LDFLAGS) $^ - -ringbuf-test.o: ringbuf-test.c ringbuf.h - $(CC) $(CFLAGS) -c $< -o $@ - -ringbuf.o: ringbuf.c ringbuf.h - $(CC) $(CFLAGS) -c $< -o $@ - -clean: - rm -f ringbuf-test ringbuf-test-gcov *.o *.gcov *.gcda *.gcno - -.PHONY: clean diff --git a/singe/thirdparty/c-ringbuf/README.md b/singe/thirdparty/c-ringbuf/README.md deleted file mode 100644 index b0faa82e8..000000000 --- a/singe/thirdparty/c-ringbuf/README.md +++ /dev/null @@ -1,37 +0,0 @@ -# WHAT - -`c-ringbuf` is a simple ring buffer implementation in C. - -It includes support for `read(2)` and `write(2)` operations on ring buffers, `memcpy`'s into and out of ring buffers, setting the buffer contents to a constant value, and copies between ring buffers. It also supports searching for single characters, for use with line-oriented or character-delimited network protocols. - -It should be fairly straightforward to extend `c-ringbuf` to support other C library operations that operate on buffers, e.g., `recv(2)`. - -# WHY - -I implemented `c-ringbuf` because I needed a simple, dependency-free ring buffer type for use with network services written in C. - -# INSTALLING - -`c-ringbuf` is not a library as such, so it doesn't need to be installed. Just copy the `ringbuf.[ch]` source files into your project. (Also see [LICENSE](#license) below.) - -`c-ringbuf` has no dependencies beyond an ISO C90 standard library. - -Note that `ringbuf.c` contains several `assert()` statements. These are intended for use with the test harness (see below), and should probably be removed from production code, once you're confident that `c-ringbuf` works as intended. - -This distribution includes source for a test program executable (`ringbuf-test.c`), which runs extensive unit tests on the `c-ringbuf` implementation. On most platforms (other than Windows, which is not supported), you should be able to type `make` to run the unit tests. Note that the [Makefile](Makefile) uses the `clang` C compiler by default, but also has support for `gcc` -- just edit the [Makefile](Makefile) so that it uses `gcc` instead of `clang`. - -The [Makefile](Makefile) also includes targets for `gcov` coverage testing and `valgrind` memory testing, assuming you have those tools installed on your system. - -# LICENSE - -`c-ringbuf` has no license; it is dedicated to the public domain. See the file [COPYING](COPYING), included in this distribution, for the specifics. - -# CONTRIBUTING - -If you would like to make a pull request (PR) or other contribution to this project, please see [CONTRIBUTING.md](.github/CONTRIBUTING.md) for details on how to do that. - -# CONTACT - -Drew Hess - -https://drewhess.com/ diff --git a/singe/thirdparty/c-ringbuf/ringbuf-test.c b/singe/thirdparty/c-ringbuf/ringbuf-test.c deleted file mode 100644 index a30ad23c1..000000000 --- a/singe/thirdparty/c-ringbuf/ringbuf-test.c +++ /dev/null @@ -1,2204 +0,0 @@ -/* - * test-ringbuf.c - unit tests for C ring buffer implementation. - * - * Written in 2011 by Drew Hess . - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software to - * the public domain worldwide. This software is distributed without - * any warranty. - * - * You should have received a copy of the CC0 Public Domain Dedication - * along with this software. If not, see - * . - */ - -#include -#include -#include -#include -#include -#include -#include -#include "ringbuf.h" - -/* - * Fill a buffer with a test pattern. - */ -void * -fill_buffer(void *buf, size_t buf_size, const char *test_pattern) -{ - size_t pattern_size = strlen(test_pattern); - size_t nblocks = buf_size / pattern_size; - uint8_t *p = buf; - size_t n; - for (n = 0; n != nblocks; ++n, p += pattern_size) - memcpy(p, (const void *) test_pattern, pattern_size); - memcpy(p, (const void *) test_pattern, buf_size % pattern_size); - - return buf; -} - -int rdfd = -1; -int wrfd = -1; -char rd_template[] = "/tmp/tmpXXXXXXringbuf"; -char wr_template[] = "/tmp/tmpXXXXXXringbuf-wr"; - -void -cleanup() -{ - if (rdfd != -1) { - close(rdfd); - unlink(rd_template); - } - if (wrfd != -1) { - close(wrfd); - unlink(wr_template); - } -} - -void -sigabort(int unused) -{ - cleanup(); - exit(1); -} - -#define START_NEW_TEST(test_num) \ - fprintf(stderr, "Test %d...", (++test_num)); - -#define END_TEST(test_num) \ - fprintf(stderr, "pass.\n"); - -/* Default size for these tests. */ -#define RINGBUF_SIZE 4096 - -int -main(int argc, char **argv) -{ - if (atexit(cleanup) == -1) { - fprintf(stderr, "Can't install atexit handler, exiting.\n"); - exit(98); - } - - /* - * catch SIGABRT when asserts fail for proper test file - * cleanup. - */ - struct sigaction sa, osa; - sa.sa_handler = sigabort; - sigemptyset(&sa.sa_mask); - sa.sa_flags = 0; - if (sigaction(SIGABRT, &sa, &osa) == -1) { - fprintf(stderr, "Can't install SIGABRT handler, exiting.\n"); - exit(99); - } - - ringbuf_t rb1 = ringbuf_new(RINGBUF_SIZE - 1); - - int test_num = 0; - - /* - * N.B.: these tests check both the ringbuf_t interface *and* a - * particular implementation. They are not black-box tests. If you - * make changes to the ringbuf_t implementation, some of these - * tests may break. - */ - - /* We use the base pointer for some implementation testing. */ - - const uint8_t *rb1_base = ringbuf_head(rb1); - - /* Initial conditions */ - START_NEW_TEST(test_num); - assert(ringbuf_buffer_size(rb1) == RINGBUF_SIZE); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - END_TEST(test_num); - - /* freeing a ring buffer sets the pointer to 0 */ - START_NEW_TEST(test_num); - ringbuf_free(&rb1); - assert(!rb1); - END_TEST(test_num); - - /* Different sizes */ - rb1 = ringbuf_new(24); - rb1_base = ringbuf_head(rb1); - - START_NEW_TEST(test_num); - assert(ringbuf_buffer_size(rb1) == 25); - assert(ringbuf_capacity(rb1) == 24); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - ringbuf_free(&rb1); - assert(!rb1); - END_TEST(test_num); - - rb1 = ringbuf_new(RINGBUF_SIZE - 1); - rb1_base = ringbuf_head(rb1); - - /* ringbuf_reset tests */ - START_NEW_TEST(test_num); - ringbuf_memset(rb1, 1, 8); - ringbuf_reset(rb1); - assert(ringbuf_buffer_size(rb1) == RINGBUF_SIZE); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); /* overflow */ - ringbuf_reset(rb1); - assert(ringbuf_buffer_size(rb1) == RINGBUF_SIZE); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - END_TEST(test_num); - - /* ringbuf_memset with zero count */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 1, 0) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - END_TEST(test_num); - - uint8_t *buf = malloc(RINGBUF_SIZE * 2); - memset(buf, 57, RINGBUF_SIZE); - memset(buf + RINGBUF_SIZE, 58, RINGBUF_SIZE); - - /* ringbuf_memset a few bytes of data */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, 7) == 7); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 7); - assert(ringbuf_bytes_used(rb1) == 7); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) buf, ringbuf_tail(rb1), 7) == 0); - END_TEST(test_num); - - /* ringbuf_memset full capacity */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_memset, twice */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, 7) == 7); - assert(ringbuf_memset(rb1, 57, 15) == 15); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb1) == 7 + 15); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - (7 + 15)); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) buf, ringbuf_tail(rb1), 7 + 15) == 0); - END_TEST(test_num); - - /* ringbuf_memset, twice (to full capacity) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, RINGBUF_SIZE - 2) == RINGBUF_SIZE - 2); - assert(ringbuf_memset(rb1, 57, 1) == 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) buf, ringbuf_tail(rb1), RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_memset, overflow by 1 byte */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, RINGBUF_SIZE) == RINGBUF_SIZE); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - /* head should point to the beginning of the buffer */ - assert(ringbuf_head(rb1) == rb1_base); - /* tail should have bumped forward by 1 byte */ - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_memset, twice (overflow by 1 byte on 2nd copy) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(ringbuf_memset(rb1, 57, 1) == 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - /* head should point to the beginning of the buffer */ - assert(ringbuf_head(rb1) == rb1_base); - /* tail should have bumped forward by 1 byte */ - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* - * ringbuf_memset, attempt to overflow by 2 bytes, but - * ringbuf_memset will stop at 1 byte overflow (length clamping, - * see ringbuf_memset documentation). - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, RINGBUF_SIZE + 1) == RINGBUF_SIZE); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_head(rb1) == rb1_base); - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* - * ringbuf_memset, twice, overflowing both times. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(ringbuf_memset(rb1, 57, RINGBUF_SIZE) == RINGBUF_SIZE); - assert(ringbuf_memset(rb1, 58, RINGBUF_SIZE) == RINGBUF_SIZE); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_head(rb1) == rb1_base); - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + RINGBUF_SIZE, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* - * The length of test_pattern should not fit naturally into - * RINGBUF_SIZE, or else it won't be possible to detect proper - * wrapping of the head pointer. - */ - const char test_pattern[] = "abcdefghijk"; - assert((strlen(test_pattern) % RINGBUF_SIZE) != 0); - fill_buffer(buf, RINGBUF_SIZE * 2, test_pattern); - - /* ringbuf_memcpy_into with zero count */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 0) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_memcpy_into a few bytes of data */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, strlen(test_pattern)) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - strlen(test_pattern)); - assert(ringbuf_bytes_used(rb1) == strlen(test_pattern)); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp(test_pattern, ringbuf_tail(rb1), strlen(test_pattern)) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_memcpy_into full capacity */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf, RINGBUF_SIZE - 1) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_memcpy_into, twice */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, strlen(test_pattern)) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + strlen(test_pattern), strlen(test_pattern) - 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - (2 * strlen(test_pattern) - 1)); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) buf, ringbuf_tail(rb1), 2 * strlen(test_pattern) - 1) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_memcpy_into, twice (to full capacity) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 2) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + RINGBUF_SIZE - 2, 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) buf, ringbuf_tail(rb1), RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_into, overflow by 1 byte */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - /* head should point to the beginning of the buffer */ - assert(ringbuf_head(rb1) == rb1_base); - /* tail should have bumped forward by 1 byte */ - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 1, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_into, twice (overflow by 1 byte on 2nd copy) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + RINGBUF_SIZE - 1, 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - /* head should point to the beginning of the buffer */ - assert(ringbuf_head(rb1) == rb1_base); - /* tail should have bumped forward by 1 byte */ - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 1, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_into, overflow by 2 bytes (will wrap) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(ringbuf_tail(rb1) == rb1_base + 2); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 2, RINGBUF_SIZE - 2) == 0); - assert(strncmp((const char *) rb1_base, (const char *) buf + RINGBUF_SIZE, 1) == 0); - END_TEST(test_num); - - rdfd = mkstemps(rd_template, strlen("ringbuf")); - assert(rdfd != -1); - assert(write(rdfd, buf, RINGBUF_SIZE * 2) == RINGBUF_SIZE * 2); - - /* ringbuf_read with zero count */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, 0) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(*(char *)ringbuf_head(rb1) == '\1'); - assert(lseek(rdfd, 0, SEEK_CUR) == 0); - END_TEST(test_num); - - /* ringbuf_read a few bytes of data */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, strlen(test_pattern)) == strlen(test_pattern)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - strlen(test_pattern)); - assert(ringbuf_bytes_used(rb1) == strlen(test_pattern)); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp(test_pattern, ringbuf_tail(rb1), strlen(test_pattern)) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_read full capacity */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf, RINGBUF_SIZE - 1) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_read, twice */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, strlen(test_pattern)) == strlen(test_pattern)); - assert(ringbuf_read(rdfd, rb1, strlen(test_pattern) - 1) == strlen(test_pattern) - 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - (2 * strlen(test_pattern) - 1)); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) buf, ringbuf_tail(rb1), 2 * strlen(test_pattern) - 1) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_read, twice (to full capacity) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 2) == RINGBUF_SIZE - 2); - assert(ringbuf_read(rdfd, rb1, 1) == 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) buf, ringbuf_tail(rb1), RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_read, overflow by 1 byte */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE) == RINGBUF_SIZE); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - /* head should point to the beginning of the buffer */ - assert(ringbuf_head(rb1) == rb1_base); - /* tail should have bumped forward by 1 byte */ - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 1, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_read, twice (overflow by 1 byte on 2nd copy) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(ringbuf_read(rdfd, rb1, 1) == 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - /* head should point to the beginning of the buffer */ - assert(ringbuf_head(rb1) == rb1_base); - /* tail should have bumped forward by 1 byte */ - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 1, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_read, try to overflow by 2 bytes; will return a short count */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE + 1) == RINGBUF_SIZE); /* short count */ - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - /* head should point to the beginning of the buffer */ - assert(ringbuf_head(rb1) == rb1_base); - /* tail should have bumped forward by 1 byte */ - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 1, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - uint8_t *dst = malloc(RINGBUF_SIZE * 2); - - /* ringbuf_memcpy_from with zero count, empty ring buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - assert(ringbuf_memcpy_from(dst, rb1, 0) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE * 2) == 0); - END_TEST(test_num); - - /* - * The length of test_pattern2 should not fit naturally into - * RINGBUF_SIZE, or else it won't be possible to detect proper - * wrapping of the head pointer. - */ - const char test_pattern2[] = "0123456789A"; - assert((strlen(test_pattern2) % RINGBUF_SIZE) != 0); - uint8_t *buf2 = malloc(RINGBUF_SIZE * 2); - fill_buffer(buf2, RINGBUF_SIZE * 2, test_pattern2); - - /* ringbuf_memcpy_from with zero count, non-empty ring buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, test_pattern2, strlen(test_pattern2)); - assert(ringbuf_memcpy_from(dst, rb1, 0) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - strlen(test_pattern2)); - assert(ringbuf_bytes_used(rb1) == strlen(test_pattern2)); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE * 2) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_from a few bytes of data */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, test_pattern2, strlen(test_pattern2)); - assert(ringbuf_memcpy_from(dst, rb1, 3) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - (strlen(test_pattern2) - 3)); - assert(ringbuf_bytes_used(rb1) == strlen(test_pattern2) - 3); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 3); - assert(ringbuf_head(rb1) == (uint8_t *) ringbuf_tail(rb1) + (strlen(test_pattern2) - 3)); - assert(strncmp((const char *) dst, test_pattern2, 3) == 0); - assert(strncmp((const char *) dst + 3, (const char *) buf + 3, RINGBUF_SIZE * 2 - 3) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_from full capacity */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, RINGBUF_SIZE - 1); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 1) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(strncmp((const char *) dst, (const char *) buf2, RINGBUF_SIZE - 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE - 1, (const char *) buf + RINGBUF_SIZE - 1, RINGBUF_SIZE + 1) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_from, twice */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, 13); - assert(ringbuf_memcpy_from(dst, rb1, 9) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_from((uint8_t *) dst + 9, rb1, 4) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 13); - assert(strncmp((const char *) dst, (const char *) buf2, 13) == 0); - assert(strncmp((const char *) dst + 13, (const char *) buf + 13, RINGBUF_SIZE * 2 - 13) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_from, twice (full capacity) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, RINGBUF_SIZE - 1); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 2) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_from(dst + RINGBUF_SIZE - 2, rb1, 1) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(strncmp((const char *) dst, (const char *) buf2, RINGBUF_SIZE - 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE - 1, (const char *) buf + RINGBUF_SIZE - 1, RINGBUF_SIZE * 2 - (RINGBUF_SIZE -1)) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_from, attempt to underflow */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, 15); - assert(ringbuf_memcpy_from(dst, rb1, 16) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 15); - assert(ringbuf_bytes_used(rb1) == 15); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_head(rb1) == rb1_base + 15); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE * 2) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_from, attempt to underflow on 2nd call */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, 15); - assert(ringbuf_memcpy_from(dst, rb1, 14) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_from(dst + 14, rb1, 2) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 1); - assert(ringbuf_bytes_used(rb1) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 14); - assert(ringbuf_head(rb1) == rb1_base + 15); - assert(strncmp((const char *) dst, (const char *) buf2, 14) == 0); - assert(strncmp((const char *) dst + 14, (const char *) buf + 14, RINGBUF_SIZE * 2 - 14) == 0); - END_TEST(test_num); - - wrfd = mkstemps(wr_template, strlen("ringbuf-wr")); - assert(wrfd != -1); - - /* ringbuf_write with zero count, empty ring buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(ringbuf_write(wrfd, rb1, 0) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - /* should return 0 (EOF) */ - assert(read(wrfd, dst, 10) == 0); - END_TEST(test_num); - - /* ringbuf_write with zero count, non-empty ring buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - ringbuf_memcpy_into(rb1, test_pattern2, strlen(test_pattern2)); - assert(ringbuf_write(wrfd, rb1, 0) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - strlen(test_pattern2)); - assert(ringbuf_bytes_used(rb1) == strlen(test_pattern2)); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - /* should return 0 (EOF) */ - assert(read(wrfd, dst, 10) == 0); - END_TEST(test_num); - - /* ringbuf_write a few bytes of data */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, test_pattern2, strlen(test_pattern2)); - assert(ringbuf_write(wrfd, rb1, 3) == 3); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - (strlen(test_pattern2) - 3)); - assert(ringbuf_bytes_used(rb1) == strlen(test_pattern2) - 3); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 3); - assert(ringbuf_head(rb1) == (uint8_t *) ringbuf_tail(rb1) + (strlen(test_pattern2) - 3)); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 4) == 3); - assert(read(wrfd, dst + 3, 1) == 0); - assert(strncmp((const char *) dst, test_pattern2, 3) == 0); - assert(strncmp((const char *) dst + 3, (const char *) buf + 3, RINGBUF_SIZE * 2 - 3) == 0); - END_TEST(test_num); - - /* ringbuf_write full capacity */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, RINGBUF_SIZE - 1); - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, RINGBUF_SIZE) == RINGBUF_SIZE - 1); - assert(read(wrfd, dst + RINGBUF_SIZE - 1, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf2, RINGBUF_SIZE - 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE - 1, (const char *) buf + RINGBUF_SIZE - 1, RINGBUF_SIZE + 1) == 0); - END_TEST(test_num); - - /* ringbuf_write, twice */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, 13); - assert(ringbuf_write(wrfd, rb1, 9) == 9); - assert(ringbuf_write(wrfd, rb1, 4) == 4); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 13); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 14) == 13); - assert(read(wrfd, dst + 13, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf2, 13) == 0); - assert(strncmp((const char *) dst + 13, (const char *) buf + 13, RINGBUF_SIZE * 2 - 13) == 0); - END_TEST(test_num); - - /* ringbuf_write, twice (full capacity) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, RINGBUF_SIZE - 1); - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 2) == RINGBUF_SIZE - 2); - assert(ringbuf_write(wrfd, rb1, 1) == 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(read(wrfd, dst + RINGBUF_SIZE - 1, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf2, RINGBUF_SIZE - 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE - 1, (const char *) buf + RINGBUF_SIZE - 1, RINGBUF_SIZE * 2 - (RINGBUF_SIZE -1)) == 0); - END_TEST(test_num); - - /* ringbuf_write, attempt to underflow */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, 15); - assert(ringbuf_write(wrfd, rb1, 16) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 15); - assert(ringbuf_bytes_used(rb1) == 15); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_head(rb1) == rb1_base + 15); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE * 2) == 0); - END_TEST(test_num); - - /* ringbuf_write, attempt to underflow on 2nd call */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern); - ringbuf_memcpy_into(rb1, buf2, 15); - assert(ringbuf_write(wrfd, rb1, 14) == 14); - assert(ringbuf_write(wrfd, rb1, 2) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 1); - assert(ringbuf_bytes_used(rb1) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 14); - assert(ringbuf_head(rb1) == rb1_base + 15); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 15) == 14); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf2, 1) == 0); - assert(strncmp((const char *) dst + 14, (const char *) buf + 14, RINGBUF_SIZE * 2 - 14) == 0); - END_TEST(test_num); - - /* ringbuf_read followed by ringbuf_write */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 11) == 11); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, 11) == 0); - assert(strncmp((const char *) dst + 11, (const char *) buf2 + 11, RINGBUF_SIZE * 2 - 11) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_read followed by partial ringbuf_write */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - ringbuf_reset(rb1); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 7) == 7); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 4); - assert(ringbuf_bytes_used(rb1) == 4); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 11) == 7); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, 7) == 0); - assert(strncmp((const char *) dst + 7, (const char *) buf2 + 7, RINGBUF_SIZE * 2 - 7) == 0); - assert(ringbuf_tail(rb1) == rb1_base + 7); - assert(ringbuf_head(rb1) == rb1_base + 11); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* - * ringbuf_read, ringbuf_write, then ringbuf_read to just before - * the end of contiguous buffer, but don't wrap - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11 - 1) == RINGBUF_SIZE - 11 - 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 11); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 11 - 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 11); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 11) == 11); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, 11) == 0); - assert(strncmp((const char *) dst + 11, (const char *) buf2 + 11, RINGBUF_SIZE * 2 - 11) == 0); - END_TEST(test_num); - - /* - * ringbuf_read, ringbuf_write, then ringbuf_read to the end of - * the contiguous buffer, which should cause the head pointer to - * wrap. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11) == RINGBUF_SIZE - 11); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 10); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 11); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 11); - assert(ringbuf_head(rb1) == rb1_base); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, 11) == 11); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, 11) == 0); - assert(strncmp((const char *) dst + 11, (const char *) buf2 + 11, RINGBUF_SIZE * 2 - 11) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except the 2nd ringbuf_read attempts to - * read 1 beyond the end of the contiguous buffer. Because - * ringbuf_read only calls read(2) at most once, it should return - * a short count (short by one byte), since wrapping around and - * continuing to fill the ring buffer would require 2 read(2) - * calls. For good measure, follow it up with a ringbuf_write that - * causes the tail pointer to stop just short of wrapping, which - * tests behavior when the ring buffer's head pointer is less than - * its tail pointer. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - /* should return a short count! */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11 + 1) == RINGBUF_SIZE - 11); - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 12) == RINGBUF_SIZE - 12); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 2); - assert(ringbuf_bytes_used(rb1) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(ringbuf_head(rb1) == rb1_base); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE - 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE - 1, (const char *) buf2 + RINGBUF_SIZE - 1, RINGBUF_SIZE * 2 - (RINGBUF_SIZE - 1)) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except when the 2nd ringbuf_read returns - * a short count, do another. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - /* should return a short count! */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11 + 1) == RINGBUF_SIZE - 11); - assert(ringbuf_read(rdfd, rb1, 1) == 1); - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 12) == RINGBUF_SIZE - 12); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 3); - assert(ringbuf_bytes_used(rb1) == 2); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE - 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE - 1, (const char *) buf2 + RINGBUF_SIZE - 1, RINGBUF_SIZE * 2 - (RINGBUF_SIZE - 1)) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except the 2nd ringbuf_write causes the - * tail pointer to wrap (just). - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - /* should return a short count! */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11 + 1) == RINGBUF_SIZE - 11); - assert(ringbuf_read(rdfd, rb1, 1) == 1); - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 11) == RINGBUF_SIZE - 11); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 2); - assert(ringbuf_bytes_used(rb1) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, RINGBUF_SIZE) == RINGBUF_SIZE); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE, (const char *) buf2 + RINGBUF_SIZE, RINGBUF_SIZE) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except the 2nd ringbuf_write returns a - * short count. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - /* should return a short count! */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11 + 1) == RINGBUF_SIZE - 11); - assert(ringbuf_read(rdfd, rb1, 1) == 1); - /* should return a short count! */ - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 10) == RINGBUF_SIZE - 11); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 2); - assert(ringbuf_bytes_used(rb1) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, RINGBUF_SIZE) == RINGBUF_SIZE); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE, (const char *) buf2 + RINGBUF_SIZE, RINGBUF_SIZE) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except do a 3rd ringbuf_write after the - * 2nd returns the short count. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - /* should return a short count! */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11 + 1) == RINGBUF_SIZE - 11); - assert(ringbuf_read(rdfd, rb1, 1) == 1); - /* should return a short count! */ - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 10) == RINGBUF_SIZE - 11); - assert(ringbuf_write(wrfd, rb1, 1) == 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(read(wrfd, dst, RINGBUF_SIZE + 1) == RINGBUF_SIZE + 1); - assert(read(wrfd, dst, 1) == 0); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE + 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE + 1, (const char *) buf2 + RINGBUF_SIZE + 1, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* ringbuf_memcpy_into followed by ringbuf_memcpy_from */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(strncmp((const char *) dst, (const char *) buf, 11) == 0); - assert(strncmp((const char *) dst + 11, (const char *) buf2 + 11, RINGBUF_SIZE * 2 - 11) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_memcpy_into followed by partial ringbuf_memcpy_from */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 7) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 4); - assert(ringbuf_bytes_used(rb1) == 4); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(strncmp((const char *) dst, (const char *) buf, 7) == 0); - assert(strncmp((const char *) dst + 7, (const char *) buf2 + 7, RINGBUF_SIZE * 2 - 7) == 0); - assert(ringbuf_tail(rb1) == rb1_base + 7); - assert(ringbuf_head(rb1) == rb1_base + 11); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* - * ringbuf_memcpy_into, ringbuf_memcpy_from, then - * ringbuf_memcpy_into to just before the end of contiguous - * buffer, but don't wrap - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + 11, RINGBUF_SIZE - 11 - 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 11); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 11 - 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 11); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(strncmp((const char *) dst, (const char *) buf, 11) == 0); - assert(strncmp((const char *) dst + 11, (const char *) buf2 + 11, RINGBUF_SIZE * 2 - 11) == 0); - END_TEST(test_num); - - /* - * ringbuf_memcpy_into, ringbuf_memcpy_from, then - * ringbuf_memcpy_into to the end of the contiguous buffer, which - * should cause the head pointer to wrap. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + 11, RINGBUF_SIZE - 11) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 10); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 11); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 11); - assert(ringbuf_head(rb1) == rb1_base); - assert(strncmp((const char *) dst, (const char *) buf, 11) == 0); - assert(strncmp((const char *) dst + 11, (const char *) buf2 + 11, RINGBUF_SIZE * 2 - 11) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except the 2nd ringbuf_memcpy_into reads - * 1 beyond the end of the contiguous buffer, which causes it to - * wrap and do a 2nd memcpy from the start of the contiguous - * buffer. For good measure, follow it up with a ringbuf_write - * that causes the tail pointer to stop just short of wrapping, - * which tests behavior when the ring buffer's head pointer is - * less than its tail pointer. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + 11, RINGBUF_SIZE - 11 + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst + 11, rb1, RINGBUF_SIZE - 12) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 3); - assert(ringbuf_bytes_used(rb1) == 2); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE - 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE - 1, (const char *) buf2 + RINGBUF_SIZE - 1, RINGBUF_SIZE * 2 - (RINGBUF_SIZE - 1)) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except the 2nd ringbuf_write causes the - * tail pointer to wrap (just). - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + 11, RINGBUF_SIZE - 11 + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst + 11, rb1, RINGBUF_SIZE - 11) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 2); - assert(ringbuf_bytes_used(rb1) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE, (const char *) buf2 + RINGBUF_SIZE, RINGBUF_SIZE) == 0); - END_TEST(test_num); - - /* - * Same as previous test, except the 2nd ringbuf_write performs 2 - * memcpy's, the 2nd of which starts from the beginning of the - * contiguous buffer after the wrap. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_into(rb1, buf + 11, RINGBUF_SIZE - 11 + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst + 11, rb1, RINGBUF_SIZE - 11 + 1) == ringbuf_tail(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb1) == 0); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(strncmp((const char *) dst, (const char *) buf, RINGBUF_SIZE + 1) == 0); - assert(strncmp((const char *) dst + RINGBUF_SIZE + 1, (const char *) buf2 + RINGBUF_SIZE + 1, RINGBUF_SIZE - 1) == 0); - END_TEST(test_num); - - /* - * Overflow with ringbuf_read when tail pointer is > head - * pointer. Should bump tail pointer to head + 1. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - /* wrap head */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11) == RINGBUF_SIZE - 11); - /* overflow */ - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_head(rb1) == rb1_base + 11); - assert(ringbuf_tail(rb1) == rb1_base + 12); - END_TEST(test_num); - - /* - * Overflow with ringbuf_read when tail pointer is > head pointer, - * and tail pointer is at the end of the contiguous buffer. Should - * wrap tail pointer to beginning of contiguous buffer. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ftruncate(wrfd, 0) == 0); - assert(lseek(wrfd, 0, SEEK_SET) == 0); - assert(lseek(rdfd, 0, SEEK_SET) == 0); - assert(ringbuf_read(rdfd, rb1, 11) == 11); - assert(ringbuf_write(wrfd, rb1, 11) == 11); - /* wrap head */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 11) == RINGBUF_SIZE - 11); - /* write until tail points to end of contiguous buffer */ - assert(ringbuf_write(wrfd, rb1, RINGBUF_SIZE - 12) == RINGBUF_SIZE - 12); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 1); - /* overflow */ - assert(ringbuf_read(rdfd, rb1, RINGBUF_SIZE - 1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(ringbuf_tail(rb1) == rb1_base); - END_TEST(test_num); - - /* - * Overflow with ringbuf_memcpy_into when tail pointer is > head - * pointer. Should bump tail pointer to head + 1. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - /* wrap head */ - assert(ringbuf_memcpy_into(rb1, buf + 11, RINGBUF_SIZE - 11) == ringbuf_head(rb1)); - /* overflow */ - assert(ringbuf_memcpy_into(rb1, buf + RINGBUF_SIZE, 11) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_head(rb1) == rb1_base + 11); - assert(ringbuf_tail(rb1) == rb1_base + 12); - END_TEST(test_num); - - /* - * Overflow with ringbuf_memcpy_into when tail pointer is > head - * pointer, and tail pointer is at the end of the contiguous - * buffer. Should wrap tail pointer to beginning of contiguous - * buffer. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - fill_buffer(dst, RINGBUF_SIZE * 2, test_pattern2); - assert(ringbuf_memcpy_into(rb1, buf, 11) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 11) == ringbuf_tail(rb1)); - /* wrap head */ - assert(ringbuf_memcpy_into(rb1, buf + 11, RINGBUF_SIZE - 11) == ringbuf_head(rb1)); - /* copy from until tail points to end of contiguous buffer */ - assert(ringbuf_memcpy_from(dst + 11, rb1, RINGBUF_SIZE - 12) == ringbuf_tail(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 1); - /* overflow */ - assert(ringbuf_memcpy_into(rb1, buf + RINGBUF_SIZE, RINGBUF_SIZE - 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(ringbuf_tail(rb1) == rb1_base); - END_TEST(test_num); - - ringbuf_t rb2 = ringbuf_new(RINGBUF_SIZE - 1); - const uint8_t *rb2_base = ringbuf_head(rb2); - - /* ringbuf_copy with zero count, empty buffers */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_copy(rb1, rb2, 0) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2)); - assert(ringbuf_bytes_used(rb1) == 0); - assert(ringbuf_bytes_used(rb2) == 0); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb2) == ringbuf_head(rb2)); - assert(ringbuf_head(rb1) == rb1_base); - assert(ringbuf_head(rb2) == rb2_base); - END_TEST(test_num); - - /* ringbuf_copy with zero count, empty src */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_copy(rb1, rb2, 0) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 2); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2)); - assert(ringbuf_bytes_used(rb1) == 2); - assert(ringbuf_bytes_used(rb2) == 0); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_tail(rb2) == ringbuf_head(rb2)); - assert(ringbuf_head(rb1) == rb1_base + 2); - assert(ringbuf_head(rb2) == rb2_base); - END_TEST(test_num); - - /* ringbuf_copy with zero count, empty dst */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb2, buf, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 0) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2) - 2); - assert(ringbuf_bytes_used(rb1) == 0); - assert(ringbuf_bytes_used(rb2) == 2); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(ringbuf_is_empty(rb1)); - assert(!ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == ringbuf_head(rb1)); - assert(ringbuf_tail(rb2) == rb2_base); - assert(ringbuf_head(rb1) == rb1_base); - assert(ringbuf_head(rb2) == rb2_base + 2); - END_TEST(test_num); - - /* ringbuf_copy with zero count */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_into(rb2, buf2, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 0) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 2); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2) - 2); - assert(ringbuf_bytes_used(rb1) == 2); - assert(ringbuf_bytes_used(rb2) == 2); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(!ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_tail(rb2) == rb2_base); - assert(ringbuf_head(rb1) == rb1_base + 2); - assert(ringbuf_head(rb2) == rb2_base + 2); - END_TEST(test_num); - - /* ringbuf_copy full contents of rb2 into rb1 (initially empty) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb2, buf2, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 2) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 2); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2)); - assert(ringbuf_bytes_used(rb1) == 2); - assert(ringbuf_bytes_used(rb2) == 0); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_tail(rb2) == rb2_base + 2); - assert(ringbuf_head(rb1) == rb1_base + 2); - assert(ringbuf_head(rb2) == rb2_base + 2); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf2, 2) == 0); - END_TEST(test_num); - - /* ringbuf_copy full contents of rb2 into rb1 (latter initially - * has 3 bytes) */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb1, buf, 3) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_into(rb2, buf2, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 2) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 5); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2)); - assert(ringbuf_bytes_used(rb1) == 5); - assert(ringbuf_bytes_used(rb2) == 0); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_tail(rb2) == rb2_base + 2); - assert(ringbuf_head(rb1) == rb1_base + 5); - assert(ringbuf_head(rb2) == rb2_base + 2); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf, 3) == 0); - assert(strncmp((const char *) ringbuf_tail(rb1) + 3, (const char *) buf2, 2) == 0); - END_TEST(test_num); - - /* ringbuf_copy, wrap head of dst */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 1) == ringbuf_head(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - /* make sure rb1 doesn't overflow on later ringbuf_copy */ - assert(ringbuf_memcpy_from(dst, rb1, 1) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_into(rb2, buf2, 1) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 1) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2)); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb2) == 0); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base + 1); - assert(ringbuf_tail(rb2) == rb2_base + 1); - assert(ringbuf_head(rb1) == rb1_base); - assert(ringbuf_head(rb2) == rb2_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 1, RINGBUF_SIZE - 2) == 0); - assert(strncmp((const char *) ringbuf_tail(rb1) + RINGBUF_SIZE - 2, (const char *) buf2, 1) == 0); - END_TEST(test_num); - - /* ringbuf_copy, wrap head of dst and continue copying into start - * of contiguous buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 1) == ringbuf_head(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - /* make sure rb1 doesn't overflow on later ringbuf_copy */ - assert(ringbuf_memcpy_from(dst, rb1, 2) == ringbuf_tail(rb1)); - assert(ringbuf_memcpy_into(rb2, buf2, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 2) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2)); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb2) == 0); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base + 2); - assert(ringbuf_tail(rb2) == rb2_base + 2); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(ringbuf_head(rb2) == rb2_base + 2); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 2, RINGBUF_SIZE - 3) == 0); - /* last position in contiguous buffer */ - assert(strncmp((const char *) ringbuf_tail(rb1) + RINGBUF_SIZE - 3, (const char *) buf2, 1) == 0); - /* start of contiguous buffer (from copy wrap) */ - assert(strncmp((const char *) rb1_base, (const char *) buf2 + 1, 1) == 0); - END_TEST(test_num); - - /* ringbuf_copy, wrap tail of src */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb2, buf2, RINGBUF_SIZE - 1) == ringbuf_head(rb2)); - assert(ringbuf_head(rb2) == rb2_base + RINGBUF_SIZE - 1); - assert(ringbuf_memcpy_from(dst, rb2, RINGBUF_SIZE - 3) == ringbuf_tail(rb2)); - assert(ringbuf_tail(rb2) == rb2_base + RINGBUF_SIZE - 3); - assert(ringbuf_memcpy_into(rb2, (uint8_t *) buf2 + RINGBUF_SIZE - 1, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 3) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 1 - 3); - assert(ringbuf_bytes_free(rb2) == RINGBUF_SIZE - 1 - 1); - assert(ringbuf_bytes_used(rb1) == 3); - assert(ringbuf_bytes_used(rb2) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(!ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_tail(rb2) == rb2_base); - assert(ringbuf_head(rb1) == rb1_base + 3); - assert(ringbuf_head(rb2) == rb2_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf2 + RINGBUF_SIZE - 3, 3) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_copy, wrap tail of src and continue copying from start - * of contiguous buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb2, buf2, RINGBUF_SIZE - 1) == ringbuf_head(rb2)); - assert(ringbuf_head(rb2) == rb2_base + RINGBUF_SIZE - 1); - assert(ringbuf_memcpy_from(dst, rb2, RINGBUF_SIZE - 3) == ringbuf_tail(rb2)); - assert(ringbuf_tail(rb2) == rb2_base + RINGBUF_SIZE - 3); - assert(ringbuf_memcpy_into(rb2, buf2 + RINGBUF_SIZE - 1, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 4) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 1 - 4); - assert(ringbuf_bytes_free(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb1) == 4); - assert(ringbuf_bytes_used(rb2) == 0); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_tail(rb2) == rb2_base + 1); - assert(ringbuf_head(rb1) == rb1_base + 4); - assert(ringbuf_head(rb2) == rb2_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf2 + RINGBUF_SIZE - 3, 4) == 0); - assert(*(char *)ringbuf_head(rb1) == '\1'); - END_TEST(test_num); - - /* ringbuf_copy, wrap tail of src and head of dst simultaneously, - * then continue copying from start of contiguous buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb2, buf2, RINGBUF_SIZE - 1) == ringbuf_head(rb2)); - assert(ringbuf_head(rb2) == rb2_base + RINGBUF_SIZE - 1); - assert(ringbuf_memcpy_from(dst, rb2, RINGBUF_SIZE - 3) == ringbuf_tail(rb2)); - assert(ringbuf_tail(rb2) == rb2_base + RINGBUF_SIZE - 3); - assert(ringbuf_memcpy_into(rb2, buf2 + RINGBUF_SIZE - 1, 2) == ringbuf_head(rb2)); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 3) == ringbuf_head(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 3); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 3) == ringbuf_tail(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 3); - assert(ringbuf_copy(rb1, rb2, 4) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 1 - 4); - assert(ringbuf_bytes_free(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb1) == 4); - assert(ringbuf_bytes_used(rb2) == 0); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 3); - assert(ringbuf_tail(rb2) == rb2_base + 1); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(ringbuf_head(rb2) == rb2_base + 1); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf2 + RINGBUF_SIZE - 3, 3) == 0); - assert(strncmp((const char *) rb1_base, (const char *) buf2 + RINGBUF_SIZE, 1) == 0); - END_TEST(test_num); - - /* ringbuf_copy, force 3 separate memcpy's: up to end of src, then - * up to end of dst, then copy remaining bytes. */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb2, buf2, RINGBUF_SIZE - 1) == ringbuf_head(rb2)); - assert(ringbuf_head(rb2) == rb2_base + RINGBUF_SIZE - 1); - assert(ringbuf_memcpy_from(dst, rb2, RINGBUF_SIZE - 2) == ringbuf_tail(rb2)); - assert(ringbuf_tail(rb2) == rb2_base + RINGBUF_SIZE - 2); - assert(ringbuf_memcpy_into(rb2, buf2 + RINGBUF_SIZE - 1, 5) == ringbuf_head(rb2)); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 3) == ringbuf_head(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 3); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 4); - assert(ringbuf_copy(rb1, rb2, 5) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == RINGBUF_SIZE - 1 - 6); - assert(ringbuf_bytes_free(rb2) == RINGBUF_SIZE - 1 - 1); - assert(ringbuf_bytes_used(rb1) == 6); - assert(ringbuf_bytes_used(rb2) == 1); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(!ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base + RINGBUF_SIZE - 4); - assert(ringbuf_tail(rb2) == rb2_base + 3); - assert(ringbuf_head(rb1) == rb1_base + 2); - assert(ringbuf_head(rb2) == rb2_base + 4); - /* one byte from buf */ - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + RINGBUF_SIZE - 4, 1) == 0); - /* 5 bytes from buf2, 3 at end of contiguous buffer and 2 after the wrap */ - assert(strncmp((const char *) ringbuf_tail(rb1) + 1, (const char *) buf2 + RINGBUF_SIZE - 2, 3) == 0); - assert(strncmp((const char *) rb1_base, (const char *) buf2 + RINGBUF_SIZE - 2 + 3, 2) == 0); - END_TEST(test_num); - - /* ringbuf_copy overflow */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 1) == ringbuf_head(rb1)); - assert(ringbuf_head(rb1) == rb1_base + RINGBUF_SIZE - 1); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_memcpy_into(rb2, buf2, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 2) == ringbuf_head(rb1)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == 0); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2)); - assert(ringbuf_bytes_used(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_used(rb2) == 0); - assert(ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base + 2); - assert(ringbuf_tail(rb2) == rb2_base + 2); - assert(ringbuf_head(rb1) == rb1_base + 1); - assert(ringbuf_head(rb2) == rb2_base + 2); - assert(strncmp(ringbuf_tail(rb1), (const char *) buf + 2, RINGBUF_SIZE - 1 - 2) == 0); - assert(strncmp((const char *) ringbuf_tail(rb1) + RINGBUF_SIZE - 1 - 2, (const char *) buf2, 1) == 0); - assert(strncmp((const char *) rb1_base, (const char *) buf2 + 1, 1) == 0); - END_TEST(test_num); - - /* ringbuf_copy attempted underflow */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_reset(rb2); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb2, 2, ringbuf_buffer_size(rb2)); - ringbuf_reset(rb2); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_into(rb2, buf2, 2) == ringbuf_head(rb2)); - assert(ringbuf_copy(rb1, rb2, 3) == 0); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb2) == RINGBUF_SIZE - 1); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1) - 2); - assert(ringbuf_bytes_free(rb2) == ringbuf_capacity(rb2) - 2); - assert(ringbuf_bytes_used(rb1) == 2); - assert(ringbuf_bytes_used(rb2) == 2); - assert(!ringbuf_is_full(rb1)); - assert(!ringbuf_is_full(rb2)); - assert(!ringbuf_is_empty(rb1)); - assert(!ringbuf_is_empty(rb2)); - assert(ringbuf_tail(rb1) == rb1_base); - assert(ringbuf_tail(rb2) == rb2_base); - assert(ringbuf_head(rb1) == rb1_base + 2); - assert(ringbuf_head(rb2) == rb2_base + 2); - END_TEST(test_num); - - /* ringbuf_copy, different capacities, overflow 2nd */ - START_NEW_TEST(test_num); - ringbuf_t rb3 = ringbuf_new(8); - const uint8_t *rb3_base = ringbuf_head(rb3); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - ringbuf_memset(rb3, 3, ringbuf_buffer_size(rb3)); - ringbuf_reset(rb3); - assert(ringbuf_memcpy_into(rb1, buf, 10) == ringbuf_head(rb1)); - assert(ringbuf_copy(rb3, rb1, 10) == ringbuf_head(rb3)); - assert(ringbuf_capacity(rb1) == RINGBUF_SIZE - 1); - assert(ringbuf_capacity(rb3) == 8); - assert(ringbuf_bytes_free(rb1) == ringbuf_capacity(rb1)); - assert(ringbuf_bytes_free(rb3) == 0); - assert(ringbuf_bytes_used(rb1) == 0); - assert(ringbuf_bytes_used(rb3) == 8); - assert(!ringbuf_is_full(rb1)); - assert(ringbuf_is_full(rb3)); - assert(ringbuf_is_empty(rb1)); - assert(!ringbuf_is_empty(rb3)); - assert(ringbuf_tail(rb1) == rb1_base + 10); - assert(ringbuf_tail(rb3) == rb3_base + 2); - assert(ringbuf_head(rb1) == rb1_base + 10); - assert(ringbuf_head(rb3) == rb3_base + 1); - assert(strncmp(ringbuf_tail(rb3), (const char *) buf + 2, 7) == 0); - assert(strncmp((const char *) rb3_base, (const char *) buf + 9, 1) == 0); - ringbuf_free(&rb3); - END_TEST(test_num); - - /* ringbuf_findchr */ - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_findchr(rb1, 'a', 0) == 0); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'a', 0) == 0); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'a', 1) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'b', 0) == 1); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'b', 1) == 1); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'b', 2) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 2) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 1, 0) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, strlen(test_pattern) + 1) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'a', 1) == strlen(test_pattern)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, strlen(test_pattern) + 1) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'a', strlen(test_pattern)) == strlen(test_pattern)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, strlen(test_pattern) + 1) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'a', strlen(test_pattern) + 1) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, (strlen(test_pattern) * 2) - 1) == ringbuf_head(rb1)); - assert(ringbuf_findchr(rb1, 'a', strlen(test_pattern) + 1) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 3) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 1) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'a', 0) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 3) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 1) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'b', 0) == 0); - END_TEST(test_num); - - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, 3) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, 2) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'b', 0) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - /* find 'd' in last byte of contiguous buffer */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE - 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'd', 0) == 3); - END_TEST(test_num); - - /* - * Find just before wrap with offset 1. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'd', 1) == 1); - END_TEST(test_num); - - /* - * Miss the 'd' at the end due to offset 2. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'd', 2) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - /* - * should *not* find 'a' in the first byte of the contiguous - * buffer when head wraps. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 1 byte */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'a', 0) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - /* - * Should find 'e' at first byte of contiguous buffer (i.e., - * should wrap during search). - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'e', 0) == 2); - END_TEST(test_num); - - /* - * Should find 'e' at first byte, with offset 1. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'e', 1) == 2); - END_TEST(test_num); - - /* - * Search begins at first byte due to offset 2, should find 'e'. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'e', 2) == 2); - END_TEST(test_num); - - /* - * Miss the 'e' at first byte due to offset 3. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 4) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'e', 3) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - /* - * Should *not* find the 'c' left over from overwritten contents - * (where head is currently pointing). - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 1) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'c', 0) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - /* - * Should *not* find the 'd' left over from overwritten contents. - */ - START_NEW_TEST(test_num); - ringbuf_reset(rb1); - ringbuf_memset(rb1, 1, ringbuf_buffer_size(rb1)); - ringbuf_reset(rb1); - /* head will wrap around and overflow by 2 bytes */ - assert(ringbuf_memcpy_into(rb1, buf, RINGBUF_SIZE + 1) == ringbuf_head(rb1)); - assert(ringbuf_memcpy_from(dst, rb1, RINGBUF_SIZE - 1) == ringbuf_tail(rb1)); - assert(ringbuf_findchr(rb1, 'd', 1) == ringbuf_bytes_used(rb1)); - END_TEST(test_num); - - ringbuf_free(&rb1); - ringbuf_free(&rb2); - free(buf); - free(buf2); - free(dst); - return 0; -} diff --git a/singe/thirdparty/c-ringbuf/ringbuf.c b/singe/thirdparty/c-ringbuf/ringbuf.c deleted file mode 100644 index b18ddb6cb..000000000 --- a/singe/thirdparty/c-ringbuf/ringbuf.c +++ /dev/null @@ -1,347 +0,0 @@ -/* - * ringbuf.c - C ring buffer (FIFO) implementation. - * - * Written in 2011 by Drew Hess . - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software to - * the public domain worldwide. This software is distributed without - * any warranty. - * - * You should have received a copy of the CC0 Public Domain Dedication - * along with this software. If not, see - * . - */ - -#include "ringbuf.h" - -#include -#include -#include -#include -#include -#include -#include - -/* - * The code is written for clarity, not cleverness or performance, and - * contains many assert()s to enforce invariant assumptions and catch - * bugs. Feel free to optimize the code and to remove asserts for use - * in your own projects, once you're comfortable that it functions as - * intended. - */ - -struct ringbuf_t -{ - uint8_t *buf; - uint8_t *head, *tail; - size_t size; -}; - -ringbuf_t -ringbuf_new(size_t capacity) -{ - ringbuf_t rb = malloc(sizeof(struct ringbuf_t)); - if (rb) { - - /* One byte is used for detecting the full condition. */ - rb->size = capacity + 1; - rb->buf = malloc(rb->size); - if (rb->buf) - ringbuf_reset(rb); - else { - free(rb); - return 0; - } - } - return rb; -} - -size_t -ringbuf_buffer_size(const struct ringbuf_t *rb) -{ - return rb->size; -} - -void -ringbuf_reset(ringbuf_t rb) -{ - rb->head = rb->tail = rb->buf; -} - -void -ringbuf_free(ringbuf_t *rb) -{ - assert(rb && *rb); - free((*rb)->buf); - free(*rb); - *rb = 0; -} - -size_t -ringbuf_capacity(const struct ringbuf_t *rb) -{ - return ringbuf_buffer_size(rb) - 1; -} - -/* - * Return a pointer to one-past-the-end of the ring buffer's - * contiguous buffer. You shouldn't normally need to use this function - * unless you're writing a new ringbuf_* function. - */ -static const uint8_t * -ringbuf_end(const struct ringbuf_t *rb) -{ - return rb->buf + ringbuf_buffer_size(rb); -} - -size_t -ringbuf_bytes_free(const struct ringbuf_t *rb) -{ - if (rb->head >= rb->tail) - return ringbuf_capacity(rb) - (rb->head - rb->tail); - else - return rb->tail - rb->head - 1; -} - -size_t -ringbuf_bytes_used(const struct ringbuf_t *rb) -{ - return ringbuf_capacity(rb) - ringbuf_bytes_free(rb); -} - -int -ringbuf_is_full(const struct ringbuf_t *rb) -{ - return ringbuf_bytes_free(rb) == 0; -} - -int -ringbuf_is_empty(const struct ringbuf_t *rb) -{ - return ringbuf_bytes_free(rb) == ringbuf_capacity(rb); -} - -const void * -ringbuf_tail(const struct ringbuf_t *rb) -{ - return rb->tail; -} - -const void * -ringbuf_head(const struct ringbuf_t *rb) -{ - return rb->head; -} - -/* - * Given a ring buffer rb and a pointer to a location within its - * contiguous buffer, return the a pointer to the next logical - * location in the ring buffer. - */ -static uint8_t * -ringbuf_nextp(ringbuf_t rb, const uint8_t *p) -{ - /* - * The assert guarantees the expression (++p - rb->buf) is - * non-negative; therefore, the modulus operation is safe and - * portable. - */ - assert((p >= rb->buf) && (p < ringbuf_end(rb))); - return rb->buf + ((++p - rb->buf) % ringbuf_buffer_size(rb)); -} - -size_t -ringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset) -{ - const uint8_t *bufend = ringbuf_end(rb); - size_t bytes_used = ringbuf_bytes_used(rb); - if (offset >= bytes_used) - return bytes_used; - - const uint8_t *start = rb->buf + - (((rb->tail - rb->buf) + offset) % ringbuf_buffer_size(rb)); - assert(bufend > start); - size_t n = MIN(bufend - start, bytes_used - offset); - const uint8_t *found = memchr(start, c, n); - if (found) - return offset + (found - start); - else - return ringbuf_findchr(rb, c, offset + n); -} - -size_t -ringbuf_memset(ringbuf_t dst, int c, size_t len) -{ - const uint8_t *bufend = ringbuf_end(dst); - size_t nwritten = 0; - size_t count = MIN(len, ringbuf_buffer_size(dst)); - int overflow = count > ringbuf_bytes_free(dst); - - while (nwritten != count) { - - /* don't copy beyond the end of the buffer */ - assert(bufend > dst->head); - size_t n = MIN(bufend - dst->head, count - nwritten); - memset(dst->head, c, n); - dst->head += n; - nwritten += n; - - /* wrap? */ - if (dst->head == bufend) - dst->head = dst->buf; - } - - if (overflow) { - dst->tail = ringbuf_nextp(dst, dst->head); - assert(ringbuf_is_full(dst)); - } - - return nwritten; -} - -void * -ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count) -{ - const uint8_t *u8src = src; - const uint8_t *bufend = ringbuf_end(dst); - int overflow = count > ringbuf_bytes_free(dst); - size_t nread = 0; - - while (nread != count) { - /* don't copy beyond the end of the buffer */ - assert(bufend > dst->head); - size_t n = MIN(bufend - dst->head, count - nread); - memcpy(dst->head, u8src + nread, n); - dst->head += n; - nread += n; - - /* wrap? */ - if (dst->head == bufend) - dst->head = dst->buf; - } - - if (overflow) { - dst->tail = ringbuf_nextp(dst, dst->head); - assert(ringbuf_is_full(dst)); - } - - return dst->head; -} - -ssize_t -ringbuf_read(int fd, ringbuf_t rb, size_t count) -{ - const uint8_t *bufend = ringbuf_end(rb); - size_t nfree = ringbuf_bytes_free(rb); - - /* don't write beyond the end of the buffer */ - assert(bufend > rb->head); - count = MIN(bufend - rb->head, count); - ssize_t n = read(fd, rb->head, count); - if (n > 0) { - assert(rb->head + n <= bufend); - rb->head += n; - - /* wrap? */ - if (rb->head == bufend) - rb->head = rb->buf; - - /* fix up the tail pointer if an overflow occurred */ - if (n > nfree) { - rb->tail = ringbuf_nextp(rb, rb->head); - assert(ringbuf_is_full(rb)); - } - } - - return n; -} - -void * -ringbuf_memcpy_from(void *dst, ringbuf_t src, size_t count) -{ - size_t bytes_used = ringbuf_bytes_used(src); - if (count > bytes_used) - return 0; - - uint8_t *u8dst = dst; - const uint8_t *bufend = ringbuf_end(src); - size_t nwritten = 0; - while (nwritten != count) { - assert(bufend > src->tail); - size_t n = MIN(bufend - src->tail, count - nwritten); - memcpy(u8dst + nwritten, src->tail, n); - src->tail += n; - nwritten += n; - - /* wrap ? */ - if (src->tail == bufend) - src->tail = src->buf; - } - - assert(count + ringbuf_bytes_used(src) == bytes_used); - return src->tail; -} - -ssize_t -ringbuf_write(int fd, ringbuf_t rb, size_t count) -{ - size_t bytes_used = ringbuf_bytes_used(rb); - if (count > bytes_used) - return 0; - - const uint8_t *bufend = ringbuf_end(rb); - assert(bufend > rb->head); - count = MIN(bufend - rb->tail, count); - ssize_t n = write(fd, rb->tail, count); - if (n > 0) { - assert(rb->tail + n <= bufend); - rb->tail += n; - - /* wrap? */ - if (rb->tail == bufend) - rb->tail = rb->buf; - - assert(n + ringbuf_bytes_used(rb) == bytes_used); - } - - return n; -} - -void * -ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count) -{ - size_t src_bytes_used = ringbuf_bytes_used(src); - if (count > src_bytes_used) - return 0; - int overflow = count > ringbuf_bytes_free(dst); - - const uint8_t *src_bufend = ringbuf_end(src); - const uint8_t *dst_bufend = ringbuf_end(dst); - size_t ncopied = 0; - while (ncopied != count) { - assert(src_bufend > src->tail); - size_t nsrc = MIN(src_bufend - src->tail, count - ncopied); - assert(dst_bufend > dst->head); - size_t n = MIN(dst_bufend - dst->head, nsrc); - memcpy(dst->head, src->tail, n); - src->tail += n; - dst->head += n; - ncopied += n; - - /* wrap ? */ - if (src->tail == src_bufend) - src->tail = src->buf; - if (dst->head == dst_bufend) - dst->head = dst->buf; - } - - assert(count + ringbuf_bytes_used(src) == src_bytes_used); - - if (overflow) { - dst->tail = ringbuf_nextp(dst, dst->head); - assert(ringbuf_is_full(dst)); - } - - return dst->head; -} diff --git a/singe/thirdparty/c-ringbuf/ringbuf.h b/singe/thirdparty/c-ringbuf/ringbuf.h deleted file mode 100644 index c72bfaed3..000000000 --- a/singe/thirdparty/c-ringbuf/ringbuf.h +++ /dev/null @@ -1,243 +0,0 @@ -#ifndef INCLUDED_RINGBUF_H -#define INCLUDED_RINGBUF_H - -/* - * ringbuf.h - C ring buffer (FIFO) interface. - * - * Written in 2011 by Drew Hess . - * - * To the extent possible under law, the author(s) have dedicated all - * copyright and related and neighboring rights to this software to - * the public domain worldwide. This software is distributed without - * any warranty. - * - * You should have received a copy of the CC0 Public Domain Dedication - * along with this software. If not, see - * . - */ - -/* - * A byte-addressable ring buffer FIFO implementation. - * - * The ring buffer's head pointer points to the starting location - * where data should be written when copying data *into* the buffer - * (e.g., with ringbuf_read). The ring buffer's tail pointer points to - * the starting location where data should be read when copying data - * *from* the buffer (e.g., with ringbuf_write). - */ - -#include -#include - -typedef struct ringbuf_t *ringbuf_t; - -/* - * Create a new ring buffer with the given capacity (usable - * bytes). Note that the actual internal buffer size may be one or - * more bytes larger than the usable capacity, for bookkeeping. - * - * Returns the new ring buffer object, or 0 if there's not enough - * memory to fulfill the request for the given capacity. - */ -ringbuf_t -ringbuf_new(size_t capacity); - -/* - * The size of the internal buffer, in bytes. One or more bytes may be - * unusable in order to distinguish the "buffer full" state from the - * "buffer empty" state. - * - * For the usable capacity of the ring buffer, use the - * ringbuf_capacity function. - */ -size_t -ringbuf_buffer_size(const struct ringbuf_t *rb); - -/* - * Deallocate a ring buffer, and, as a side effect, set the pointer to - * 0. - */ -void -ringbuf_free(ringbuf_t *rb); - -/* - * Reset a ring buffer to its initial state (empty). - */ -void -ringbuf_reset(ringbuf_t rb); - -/* - * The usable capacity of the ring buffer, in bytes. Note that this - * value may be less than the ring buffer's internal buffer size, as - * returned by ringbuf_buffer_size. - */ -size_t -ringbuf_capacity(const struct ringbuf_t *rb); - -/* - * The number of free/available bytes in the ring buffer. This value - * is never larger than the ring buffer's usable capacity. - */ -size_t -ringbuf_bytes_free(const struct ringbuf_t *rb); - -/* - * The number of bytes currently being used in the ring buffer. This - * value is never larger than the ring buffer's usable capacity. - */ -size_t -ringbuf_bytes_used(const struct ringbuf_t *rb); - -int -ringbuf_is_full(const struct ringbuf_t *rb); - -int -ringbuf_is_empty(const struct ringbuf_t *rb); - -/* - * Const access to the head and tail pointers of the ring buffer. - */ -const void * -ringbuf_tail(const struct ringbuf_t *rb); - -const void * -ringbuf_head(const struct ringbuf_t *rb); - -/* - * Locate the first occurrence of character c (converted to an - * unsigned char) in ring buffer rb, beginning the search at offset - * bytes from the ring buffer's tail pointer. The function returns the - * offset of the character from the ring buffer's tail pointer, if - * found. If c does not occur in the ring buffer, the function returns - * the number of bytes used in the ring buffer. - * - * Note that the offset parameter and the returned offset are logical - * offsets from the tail pointer, not necessarily linear offsets. - */ -size_t -ringbuf_findchr(const struct ringbuf_t *rb, int c, size_t offset); - -/* - * Beginning at ring buffer dst's head pointer, fill the ring buffer - * with a repeating sequence of len bytes, each of value c (converted - * to an unsigned char). len can be as large as you like, but the - * function will never write more than ringbuf_buffer_size(dst) bytes - * in a single invocation, since that size will cause all bytes in the - * ring buffer to be written exactly once each. - * - * Note that if len is greater than the number of free bytes in dst, - * the ring buffer will overflow. When an overflow occurs, the state - * of the ring buffer is guaranteed to be consistent, including the - * head and tail pointers; old data will simply be overwritten in FIFO - * fashion, as needed. However, note that, if calling the function - * results in an overflow, the value of the ring buffer's tail pointer - * may be different than it was before the function was called. - * - * Returns the actual number of bytes written to dst: len, if - * len < ringbuf_buffer_size(dst), else ringbuf_buffer_size(dst). - */ -size_t -ringbuf_memset(ringbuf_t dst, int c, size_t len); - -/* - * Copy n bytes from a contiguous memory area src into the ring buffer - * dst. Returns the ring buffer's new head pointer. - * - * It is possible to copy more data from src than is available in the - * buffer; i.e., it's possible to overflow the ring buffer using this - * function. When an overflow occurs, the state of the ring buffer is - * guaranteed to be consistent, including the head and tail pointers; - * old data will simply be overwritten in FIFO fashion, as - * needed. However, note that, if calling the function results in an - * overflow, the value of the ring buffer's tail pointer may be - * different than it was before the function was called. - */ -void * -ringbuf_memcpy_into(ringbuf_t dst, const void *src, size_t count); - -/* - * This convenience function calls read(2) on the file descriptor fd, - * using the ring buffer rb as the destination buffer for the read, - * and returns the value returned by read(2). It will only call - * read(2) once, and may return a short count. - * - * It is possible to read more data from the file descriptor than is - * available in the buffer; i.e., it's possible to overflow the ring - * buffer using this function. When an overflow occurs, the state of - * the ring buffer is guaranteed to be consistent, including the head - * and tail pointers: old data will simply be overwritten in FIFO - * fashion, as needed. However, note that, if calling the function - * results in an overflow, the value of the ring buffer's tail pointer - * may be different than it was before the function was called. - */ -ssize_t -ringbuf_read(int fd, ringbuf_t rb, size_t count); - -/* - * Copy n bytes from the ring buffer src, starting from its tail - * pointer, into a contiguous memory area dst. Returns the value of - * src's tail pointer after the copy is finished. - * - * Note that this copy is destructive with respect to the ring buffer: - * the n bytes copied from the ring buffer are no longer available in - * the ring buffer after the copy is complete, and the ring buffer - * will have n more free bytes than it did before the function was - * called. - * - * This function will *not* allow the ring buffer to underflow. If - * count is greater than the number of bytes used in the ring buffer, - * no bytes are copied, and the function will return 0. - */ -void * -ringbuf_memcpy_from(void *dst, ringbuf_t src, size_t count); - -/* - * This convenience function calls write(2) on the file descriptor fd, - * using the ring buffer rb as the source buffer for writing (starting - * at the ring buffer's tail pointer), and returns the value returned - * by write(2). It will only call write(2) once, and may return a - * short count. - * - * Note that this copy is destructive with respect to the ring buffer: - * any bytes written from the ring buffer to the file descriptor are - * no longer available in the ring buffer after the copy is complete, - * and the ring buffer will have N more free bytes than it did before - * the function was called, where N is the value returned by the - * function (unless N is < 0, in which case an error occurred and no - * bytes were written). - * - * This function will *not* allow the ring buffer to underflow. If - * count is greater than the number of bytes used in the ring buffer, - * no bytes are written to the file descriptor, and the function will - * return 0. - */ -ssize_t -ringbuf_write(int fd, ringbuf_t rb, size_t count); - -/* - * Copy count bytes from ring buffer src, starting from its tail - * pointer, into ring buffer dst. Returns dst's new head pointer after - * the copy is finished. - * - * Note that this copy is destructive with respect to the ring buffer - * src: any bytes copied from src into dst are no longer available in - * src after the copy is complete, and src will have 'count' more free - * bytes than it did before the function was called. - * - * It is possible to copy more data from src than is available in dst; - * i.e., it's possible to overflow dst using this function. When an - * overflow occurs, the state of dst is guaranteed to be consistent, - * including the head and tail pointers; old data will simply be - * overwritten in FIFO fashion, as needed. However, note that, if - * calling the function results in an overflow, the value dst's tail - * pointer may be different than it was before the function was - * called. - * - * It is *not* possible to underflow src; if count is greater than the - * number of bytes used in src, no bytes are copied, and the function - * returns 0. - */ -void * -ringbuf_copy(ringbuf_t dst, ringbuf_t src, size_t count); - -#endif /* INCLUDED_RINGBUF_H */ diff --git a/singe/thirdparty/manymouse/linux_evdev.c b/singe/thirdparty/manymouse/linux_evdev.c index 0db138c7d..36df55b46 100644 --- a/singe/thirdparty/manymouse/linux_evdev.c +++ b/singe/thirdparty/manymouse/linux_evdev.c @@ -47,7 +47,7 @@ static int poll_mouse(MouseStruct *mouse, ManyMouseEvent *outevent) while (unhandled) /* read until failure or valid event. */ { struct input_event event; - int br = read(mouse->fd, &event, sizeof (event)); + int br = (int)read(mouse->fd, &event, sizeof (event)); if (br == -1) { if (errno == EAGAIN) @@ -154,6 +154,8 @@ static int init_mouse(const char *fname, int fd) unsigned char abscaps[(ABS_MAX / 8) + 1]; unsigned char keycaps[(KEY_MAX / 8) + 1]; + (void)fname; + memset(relcaps, '\0', sizeof (relcaps)); memset(abscaps, '\0', sizeof (abscaps)); memset(keycaps, '\0', sizeof (keycaps)); @@ -260,7 +262,7 @@ static int linux_evdev_init(void) while ((dent = readdir(dirp)) != NULL) { - char fname[128]; + char fname[384]; snprintf(fname, sizeof (fname), "/dev/input/%s", dent->d_name); if (open_if_mouse(fname)) available_mice++; @@ -268,7 +270,7 @@ static int linux_evdev_init(void) closedir(dirp); - return available_mice; + return (int)available_mice; } /* linux_evdev_init */ diff --git a/singe/thirdparty/manymouse/manymouse.h b/singe/thirdparty/manymouse/manymouse.h index 870fa2155..4fbda1ab8 100644 --- a/singe/thirdparty/manymouse/manymouse.h +++ b/singe/thirdparty/manymouse/manymouse.h @@ -6,8 +6,11 @@ * This file written by Ryan C. Gordon. */ -#ifndef _INCLUDE_MANYMOUSE_H_ -#define _INCLUDE_MANYMOUSE_H_ +#ifndef INCLUDE_MANYMOUSE_H_ +#define INCLUDE_MANYMOUSE_H_ + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wempty-translation-unit" #ifdef __cplusplus extern "C" { @@ -46,6 +49,7 @@ typedef struct int (*poll)(ManyMouseEvent *event); } ManyMouseDriver; +extern const ManyMouseDriver *ManyMouseDriver_xinput2; int ManyMouse_Init(void); const char *ManyMouse_DriverName(void); @@ -57,7 +61,9 @@ int ManyMouse_PollEvent(ManyMouseEvent *event); } #endif -#endif /* !defined _INCLUDE_MANYMOUSE_H_ */ +#pragma GCC diagnostic pop + +#endif /* !defined INCLUDE_MANYMOUSE_H_ */ /* end of manymouse.h ... */ diff --git a/singe/thirdparty/manymouse/x11_xinput2.c b/singe/thirdparty/manymouse/x11_xinput2.c index f287eaed8..9db4673a1 100644 --- a/singe/thirdparty/manymouse/x11_xinput2.c +++ b/singe/thirdparty/manymouse/x11_xinput2.c @@ -219,8 +219,11 @@ static int init_mouse(MouseStruct *mouse, const XIDeviceInfo *devinfo) { if ((classes[i]->type == XIValuatorClass) && (axis < MAX_AXIS)) { - const XIValuatorClassInfo *v = (XIValuatorClassInfo*) classes[i]; - mouse->relative[axis] = (v->mode == XIModeRelative); +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + const XIValuatorClassInfo *v = (XIValuatorClassInfo*) classes[i]; +#pragma GCC diagnostic pop + mouse->relative[axis] = (v->mode == XIModeRelative); mouse->minval[axis] = (int) v->min; mouse->maxval[axis] = (int) v->max; axis++; @@ -311,7 +314,7 @@ static int x11_xinput2_init_internal(void) } /* for */ pXIFreeDeviceInfo(device_list); - return available_mice; + return (int)available_mice; } /* x11_xinput2_init_internal */ @@ -341,7 +344,7 @@ static int find_mouse_by_devid(const int devid) int i; const MouseStruct *mouse = mice; - for (i = 0; i < available_mice; i++, mouse++) + for (i = 0; i < (int)available_mice; i++, mouse++) { if (mouse->device_id == devid) return (mouse->connected) ? i : -1; @@ -435,8 +438,8 @@ static void pump_events(void) event.type = MANYMOUSE_EVENT_RELMOTION; else event.type = MANYMOUSE_EVENT_ABSMOTION; - event.device = mouse; - event.item = i; + event.device = (unsigned int)mouse; + event.item = (unsigned int)i; event.value = value; event.minval = mice[mouse].minval[i]; event.maxval = mice[mouse].maxval[i]; @@ -463,7 +466,7 @@ static void pump_events(void) if (pressed) /* ignore "up" for these "buttons" */ { event.type = MANYMOUSE_EVENT_SCROLL; - event.device = mouse; + event.device = (unsigned int)mouse; if ((button == 4) || (button == 5)) event.item = 0; @@ -481,8 +484,8 @@ static void pump_events(void) else { event.type = MANYMOUSE_EVENT_BUTTON; - event.device = mouse; - event.item = button-1; + event.device = (unsigned int)mouse; + event.item = (unsigned int)button-1; event.value = pressed; queue_event(&event); } /* else */ @@ -500,7 +503,7 @@ static void pump_events(void) { mice[mouse].connected = 0; event.type = MANYMOUSE_EVENT_DISCONNECT; - event.device = mouse; + event.device = (unsigned int)mouse; queue_event(&event); } /* if */ } /* if */ diff --git a/singe/thirdparty/utarray.h b/singe/thirdparty/utarray.h deleted file mode 100644 index cc20614d9..000000000 --- a/singe/thirdparty/utarray.h +++ /dev/null @@ -1,247 +0,0 @@ -/* -Copyright (c) 2008-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS -IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED -TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A -PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER -OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ - -/* a dynamic array implementation using macros - */ -#ifndef UTARRAY_H -#define UTARRAY_H - -#define UTARRAY_VERSION 2.1.0 - -#include /* size_t */ -#include /* memset, etc */ -#include /* exit */ - -#ifdef __GNUC__ -#define UTARRAY_UNUSED __attribute__((__unused__)) -#else -#define UTARRAY_UNUSED -#endif - -#ifdef oom -#error "The name of macro 'oom' has been changed to 'utarray_oom'. Please update your code." -#define utarray_oom() oom() -#endif - -#ifndef utarray_oom -#define utarray_oom() exit(-1) -#endif - -typedef void (ctor_f)(void *dst, const void *src); -typedef void (dtor_f)(void *elt); -typedef void (init_f)(void *elt); -typedef struct { - size_t sz; - init_f *init; - ctor_f *copy; - dtor_f *dtor; -} UT_icd; - -typedef struct { - unsigned i,n;/* i: index of next available slot, n: num slots */ - UT_icd icd; /* initializer, copy and destructor functions */ - char *d; /* n slots of size icd->sz*/ -} UT_array; - -#define utarray_init(a,_icd) do { \ - memset(a,0,sizeof(UT_array)); \ - (a)->icd = *(_icd); \ -} while(0) - -#define utarray_done(a) do { \ - if ((a)->n) { \ - if ((a)->icd.dtor) { \ - unsigned _ut_i; \ - for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ - (a)->icd.dtor(utarray_eltptr(a,_ut_i)); \ - } \ - } \ - free((a)->d); \ - } \ - (a)->n=0; \ -} while(0) - -#define utarray_new(a,_icd) do { \ - (a) = (UT_array*)malloc(sizeof(UT_array)); \ - if ((a) == NULL) { \ - utarray_oom(); \ - } \ - utarray_init(a,_icd); \ -} while(0) - -#define utarray_free(a) do { \ - utarray_done(a); \ - free(a); \ -} while(0) - -#define utarray_reserve(a,by) do { \ - if (((a)->i+(by)) > (a)->n) { \ - char *utarray_tmp; \ - while (((a)->i+(by)) > (a)->n) { (a)->n = ((a)->n ? (2*(a)->n) : 8); } \ - utarray_tmp=(char*)realloc((a)->d, (a)->n*(a)->icd.sz); \ - if (utarray_tmp == NULL) { \ - utarray_oom(); \ - } \ - (a)->d=utarray_tmp; \ - } \ -} while(0) - -#define utarray_push_back(a,p) do { \ - utarray_reserve(a,1); \ - if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,(a)->i++), p); } \ - else { memcpy(_utarray_eltptr(a,(a)->i++), p, (a)->icd.sz); }; \ -} while(0) - -#define utarray_pop_back(a) do { \ - if ((a)->icd.dtor) { (a)->icd.dtor( _utarray_eltptr(a,--((a)->i))); } \ - else { (a)->i--; } \ -} while(0) - -#define utarray_extend_back(a) do { \ - utarray_reserve(a,1); \ - if ((a)->icd.init) { (a)->icd.init(_utarray_eltptr(a,(a)->i)); } \ - else { memset(_utarray_eltptr(a,(a)->i),0,(a)->icd.sz); } \ - (a)->i++; \ -} while(0) - -#define utarray_len(a) ((a)->i) - -#define utarray_eltptr(a,j) (((j) < (a)->i) ? _utarray_eltptr(a,j) : NULL) -#define _utarray_eltptr(a,j) ((a)->d + ((a)->icd.sz * (j))) - -#define utarray_insert(a,p,j) do { \ - if ((j) > (a)->i) utarray_resize(a,j); \ - utarray_reserve(a,1); \ - if ((j) < (a)->i) { \ - memmove( _utarray_eltptr(a,(j)+1), _utarray_eltptr(a,j), \ - ((a)->i - (j))*((a)->icd.sz)); \ - } \ - if ((a)->icd.copy) { (a)->icd.copy( _utarray_eltptr(a,j), p); } \ - else { memcpy(_utarray_eltptr(a,j), p, (a)->icd.sz); }; \ - (a)->i++; \ -} while(0) - -#define utarray_inserta(a,w,j) do { \ - if (utarray_len(w) == 0) break; \ - if ((j) > (a)->i) utarray_resize(a,j); \ - utarray_reserve(a,utarray_len(w)); \ - if ((j) < (a)->i) { \ - memmove(_utarray_eltptr(a,(j)+utarray_len(w)), \ - _utarray_eltptr(a,j), \ - ((a)->i - (j))*((a)->icd.sz)); \ - } \ - if ((a)->icd.copy) { \ - unsigned _ut_i; \ - for(_ut_i=0;_ut_i<(w)->i;_ut_i++) { \ - (a)->icd.copy(_utarray_eltptr(a, (j) + _ut_i), _utarray_eltptr(w, _ut_i)); \ - } \ - } else { \ - memcpy(_utarray_eltptr(a,j), _utarray_eltptr(w,0), \ - utarray_len(w)*((a)->icd.sz)); \ - } \ - (a)->i += utarray_len(w); \ -} while(0) - -#define utarray_resize(dst,num) do { \ - unsigned _ut_i; \ - if ((dst)->i > (unsigned)(num)) { \ - if ((dst)->icd.dtor) { \ - for (_ut_i = (num); _ut_i < (dst)->i; ++_ut_i) { \ - (dst)->icd.dtor(_utarray_eltptr(dst, _ut_i)); \ - } \ - } \ - } else if ((dst)->i < (unsigned)(num)) { \ - utarray_reserve(dst, (num) - (dst)->i); \ - if ((dst)->icd.init) { \ - for (_ut_i = (dst)->i; _ut_i < (unsigned)(num); ++_ut_i) { \ - (dst)->icd.init(_utarray_eltptr(dst, _ut_i)); \ - } \ - } else { \ - memset(_utarray_eltptr(dst, (dst)->i), 0, (dst)->icd.sz*((num) - (dst)->i)); \ - } \ - } \ - (dst)->i = (num); \ -} while(0) - -#define utarray_concat(dst,src) do { \ - utarray_inserta(dst, src, utarray_len(dst)); \ -} while(0) - -#define utarray_erase(a,pos,len) do { \ - if ((a)->icd.dtor) { \ - unsigned _ut_i; \ - for (_ut_i = 0; _ut_i < (len); _ut_i++) { \ - (a)->icd.dtor(utarray_eltptr(a, (pos) + _ut_i)); \ - } \ - } \ - if ((a)->i > ((pos) + (len))) { \ - memmove(_utarray_eltptr(a, pos), _utarray_eltptr(a, (pos) + (len)), \ - ((a)->i - ((pos) + (len))) * (a)->icd.sz); \ - } \ - (a)->i -= (len); \ -} while(0) - -#define utarray_renew(a,u) do { \ - if (a) utarray_clear(a); \ - else utarray_new(a, u); \ -} while(0) - -#define utarray_clear(a) do { \ - if ((a)->i > 0) { \ - if ((a)->icd.dtor) { \ - unsigned _ut_i; \ - for(_ut_i=0; _ut_i < (a)->i; _ut_i++) { \ - (a)->icd.dtor(_utarray_eltptr(a, _ut_i)); \ - } \ - } \ - (a)->i = 0; \ - } \ -} while(0) - -#define utarray_sort(a,cmp) do { \ - qsort((a)->d, (a)->i, (a)->icd.sz, cmp); \ -} while(0) - -#define utarray_find(a,v,cmp) bsearch((v),(a)->d,(a)->i,(a)->icd.sz,cmp) - -#define utarray_front(a) (((a)->i) ? (_utarray_eltptr(a,0)) : NULL) -#define utarray_next(a,e) (((e)==NULL) ? utarray_front(a) : (((a)->i != utarray_eltidx(a,e)+1) ? _utarray_eltptr(a,utarray_eltidx(a,e)+1) : NULL)) -#define utarray_prev(a,e) (((e)==NULL) ? utarray_back(a) : ((utarray_eltidx(a,e) != 0) ? _utarray_eltptr(a,utarray_eltidx(a,e)-1) : NULL)) -#define utarray_back(a) (((a)->i) ? (_utarray_eltptr(a,(a)->i-1)) : NULL) -#define utarray_eltidx(a,e) (((char*)(e) - (a)->d) / (a)->icd.sz) - -/* last we pre-define a few icd for common utarrays of ints and strings */ -static void utarray_str_cpy(void *dst, const void *src) { - char **_src = (char**)src, **_dst = (char**)dst; - *_dst = (*_src == NULL) ? NULL : strdup(*_src); -} -static void utarray_str_dtor(void *elt) { - char **eltc = (char**)elt; - if (*eltc != NULL) free(*eltc); -} -static const UT_icd ut_str_icd UTARRAY_UNUSED = {sizeof(char*),NULL,utarray_str_cpy,utarray_str_dtor}; -static const UT_icd ut_int_icd UTARRAY_UNUSED = {sizeof(int),NULL,NULL,NULL}; -static const UT_icd ut_ptr_icd UTARRAY_UNUSED = {sizeof(void*),NULL,NULL,NULL}; - - -#endif /* UTARRAY_H */ diff --git a/singe/thirdparty/uthash.h b/singe/thirdparty/uthash.h new file mode 100644 index 000000000..a790ea59f --- /dev/null +++ b/singe/thirdparty/uthash.h @@ -0,0 +1,1230 @@ +/* +Copyright (c) 2003-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.1.0 + +#include /* memcmp, memset, strlen */ +#include /* ptrdiff_t */ +#include /* exit */ + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +/* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ +#if defined(_WIN32) +#if defined(_MSC_VER) && _MSC_VER >= 1600 +#include +#elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif +#elif defined(__GNUC__) && !defined(__VXWORKS__) +#include +#else +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifdef uthash_memcmp +/* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */ +#warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead" +#else +#define uthash_memcmp(a,b,n) memcmp(a,b,n) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FCN(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ +#ifdef HASH_FUNCTION +#define HASH_FCN HASH_FUNCTION +#else +#define HASH_FCN HASH_JEN +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +#ifdef HASH_USING_NO_STRICT_ALIASING +/* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. + * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. + * MurmurHash uses the faster approach only on CPU's where we know it's safe. + * + * Note the preprocessor built-in defines can be emitted using: + * + * gcc -m64 -dM -E - < /dev/null (on gcc) + * cc -## a.c (where a.c is a simple test file) (Sun Studio) + */ +#if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) +#define MUR_GETBLOCK(p,i) p[i] +#else /* non intel */ +#define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) +#define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) +#define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) +#define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) +#define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) +#if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) +#define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) +#else /* assume little endian non-intel */ +#define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) +#define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) +#define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) +#endif +#define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ + (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ + (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ + MUR_ONE_THREE(p)))) +#endif +#define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) +#define MUR_FMIX(_h) \ +do { \ + _h ^= _h >> 16; \ + _h *= 0x85ebca6bu; \ + _h ^= _h >> 13; \ + _h *= 0xc2b2ae35u; \ + _h ^= _h >> 16; \ +} while (0) + +#define HASH_MUR(key,keylen,hashv) \ +do { \ + const uint8_t *_mur_data = (const uint8_t*)(key); \ + const int _mur_nblocks = (int)(keylen) / 4; \ + uint32_t _mur_h1 = 0xf88D5353u; \ + uint32_t _mur_c1 = 0xcc9e2d51u; \ + uint32_t _mur_c2 = 0x1b873593u; \ + uint32_t _mur_k1 = 0; \ + const uint8_t *_mur_tail; \ + const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ + int _mur_i; \ + for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) { \ + _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + \ + _mur_h1 ^= _mur_k1; \ + _mur_h1 = MUR_ROTL32(_mur_h1,13); \ + _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ + } \ + _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ + _mur_k1=0; \ + switch ((keylen) & 3U) { \ + case 0: break; \ + case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ + case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ + case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ + _mur_k1 *= _mur_c1; \ + _mur_k1 = MUR_ROTL32(_mur_k1,15); \ + _mur_k1 *= _mur_c2; \ + _mur_h1 ^= _mur_k1; \ + } \ + _mur_h1 ^= (uint32_t)(keylen); \ + MUR_FMIX(_mur_h1); \ + hashv = _mur_h1; \ +} while (0) +#endif /* HASH_USING_NO_STRICT_ALIASING */ + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/singe/util.c b/singe/util.c new file mode 100644 index 000000000..5e69ba6ab --- /dev/null +++ b/singe/util.c @@ -0,0 +1,54 @@ +/* + * + * Singe 2 + * Copyright (C) 2019 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include +#include + +#include "common.h" +#include "util.h" + + +__attribute__((__format__(__printf__, 1, 0))) +__attribute__((noreturn)) +void utilDie(char *fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stderr, fmt, args); + va_end(args); + printf("\n"); + fflush(stderr); + exit(1); +} + + +__attribute__((__format__(__printf__, 1, 0))) +void utilSay(char *fmt, ...) { + va_list args; + va_start(args, fmt); + vfprintf(stdout, fmt, args); + va_end(args); + printf("\n"); + fflush(stdout); +} + + diff --git a/singe/util.h b/singe/util.h new file mode 100644 index 000000000..f9a63d549 --- /dev/null +++ b/singe/util.h @@ -0,0 +1,29 @@ +/* + * + * Singe 2 + * Copyright (C) 2019 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef UTIL_H +#define UTIL_H + +void utilDie(char *fmt, ...); +void utilSay(char *fmt, ...); + +#endif // UTIL_H diff --git a/singe/videoPlayer.c b/singe/videoPlayer.c new file mode 100644 index 000000000..05511d45c --- /dev/null +++ b/singe/videoPlayer.c @@ -0,0 +1,469 @@ +/* + * + * Singe 2 + * Copyright (C) 2019 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#include +#include + +#include "thirdparty/uthash.h" + +#include "common.h" +#include "util.h" +#include "videoPlayer.h" + + +#define AUDIO_STREAM_LOW_WATERMARK (32 * 1024) +#define AUDIO_SAMPLE_PREREAD 1024 +#define AUDIO_SILENCE_SECONDS 2 + + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wpadded" +typedef struct VideoPlayerS { + int id; + bool playing; + bool resetTime; + byte *audioBuffer; + byte audioSampleBytes; + byte *audioSilenceRaw; + char errMsg[1024]; + int audioTrack; + int videoTrack; + int frame; + int audioSampleSize; + int mixSampleSize; + int audioSilenceChannel; + int64_t audioBufferSize; + int64_t frameDeltaTime; + int64_t lastFrameTime; + int64_t timestamp; + int64_t audioPosition; + Uint16 audioFormat; + Uint32 lastTickTime; + Uint32 audioSilenceSize; + Mix_Chunk *silenceChunk; + SDL_AudioStream *audioStream; + SDL_Texture *videoTexture; + FFMS_ErrorInfo errInfo; + FFMS_AudioSource *audioSource; + FFMS_VideoSource *videoSource; + const FFMS_AudioProperties *audioProps; + const FFMS_VideoProperties *videoProps; + const FFMS_Frame *propFrame; + const FFMS_TrackTimeBase *videoTimeBase; + const FFMS_Frame *frameData; + const FFMS_FrameInfo *frameInfo; + UT_hash_handle hh; +} VideoPlayerT; +#pragma GCC diagnostic pop + + +void _dequeueVideoAudio(int channel, void *stream, int len, void *udata); +int FFMS_CC _indexCallBack(int64_t Current, int64_t Total, void *ICPrivate); + + +static VideoPlayerT *_videoPlayerHash = NULL; +static int _nextId = 0; +static int _mixRate = -1; +static Uint8 _mixChannels = 0; +static SDL_AudioFormat _mixFormat = 0; + + +void _dequeueVideoAudio(int channel, void *stream, int bytes, void *udata) { + VideoPlayerT *v = (VideoPlayerT *)udata; + int bytesToCopy = bytes; + int available = SDL_AudioStreamAvailable(v->audioStream); + int remainder = 0; + + (void)channel; + + // Don't copy more than we have room for + if (bytesToCopy > available) { + bytesToCopy = available; + } + + // Ensure we only copy complete samples (Is this needed?) + remainder = bytesToCopy % v->audioSampleSize; + bytesToCopy -= remainder; + + if (SDL_AudioStreamGet(v->audioStream, stream, bytesToCopy) < 0) utilDie("%s", SDL_GetError()); +} + + +int FFMS_CC _indexCallBack(int64_t current, int64_t total, void *ICPrivate) { + static int lastPercent = 0; + int thisPercent = 0; + + (void)ICPrivate; // Contains current VideoPlayerT + + if ((current == 0) && (total == 0)) { + lastPercent = 0; // Reset + } else { + thisPercent = (int)((double)current / (double)total * 100.0); + if (thisPercent != lastPercent) { + lastPercent = thisPercent; + utilSay("Indexing: %d%%", thisPercent); + } + } + + return 0; +} + + +int videoInit(void) { + + int channels = _mixChannels; + + // Start FFMS + FFMS_Init(0, 0); + + // Fetch mixer settings + if (!Mix_QuerySpec(&_mixRate, &_mixFormat, &channels)) utilDie("%s", Mix_GetError()); + _mixChannels = (Uint8)channels; + + return 0; +} + + +int videoIsPlaying(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoIsPlaying.", playerIndex); + + return v->playing; +} + + +int videoLoad(char *filename, SDL_Renderer *renderer) { + char indexName[1024]; + int pixelFormats[2]; + int result = -1; + FFMS_Index *index = NULL; + FFMS_Indexer *indexer = NULL; + VideoPlayerT *v = NULL; + + // Create new videoPlayer + v = calloc(1, sizeof(VideoPlayerT)); + if (!v) utilDie("Unable to allocate new video player."); + + // Set some starting values + v->audioTrack = -1; + v->videoTrack = -1; + v->audioSilenceChannel = -1; + v->playing = false; // Start paused + v->errInfo.Buffer = v->errMsg; + v->errInfo.BufferSize = sizeof(v->errMsg); + v->errInfo.ErrorType = FFMS_ERROR_SUCCESS; + v->errInfo.SubType = FFMS_ERROR_SUCCESS; + + // Index file + snprintf(indexName, 1024, "%s.index", filename); + index = FFMS_ReadIndex(indexName, &v->errInfo); + if (index) { + if (FFMS_IndexBelongsToFile(index, filename, &v->errInfo)) { + FFMS_DestroyIndex(index); + index = NULL; + utilSay("Cached index is invalid."); + } else { + utilSay("Loaded cached index."); + } + } + if (!index) { + utilSay("Creating new index."); + indexer = FFMS_CreateIndexer(filename, &v->errInfo); + if (indexer == NULL) utilDie("%s", v->errInfo.Buffer); + FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_AUDIO, 1, 0); + FFMS_TrackTypeIndexSettings(indexer, FFMS_TYPE_VIDEO, 1, 0); + _indexCallBack(0, 0, v); + FFMS_SetProgressCallback(indexer, _indexCallBack, v); + index = FFMS_DoIndexing2(indexer, FFMS_IEH_ABORT, &v->errInfo); + if (index == NULL) utilDie("%s", v->errInfo.Buffer); + if (FFMS_WriteIndex(indexName, index, &v->errInfo)) utilDie("%s", v->errInfo.Buffer); + } + + // Find video track + v->videoTrack = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_VIDEO, &v->errInfo); + if (v->videoTrack < 0) utilDie("%s", v->errInfo.Buffer); + v->videoSource = FFMS_CreateVideoSource(filename, v->videoTrack, index, 1, FFMS_SEEK_NORMAL, &v->errInfo); + if (v->videoSource == NULL) utilDie("%s", v->errInfo.Buffer); + + // Get video properties + v->videoProps = FFMS_GetVideoProperties(v->videoSource); + v->propFrame = FFMS_GetFrame(v->videoSource, 0, &v->errInfo); + if (v->propFrame == NULL) utilDie("%s", v->errInfo.Buffer); + v->videoTimeBase = FFMS_GetTimeBase(FFMS_GetTrackFromVideo(v->videoSource)); + + // Set up output video format + pixelFormats[0] = FFMS_GetPixFmt("bgra"); + pixelFormats[1] = -1; + if (FFMS_SetOutputFormatV2(v->videoSource, pixelFormats, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight, FFMS_RESIZER_BICUBIC, &v->errInfo)) utilDie("%s", v->errInfo.Buffer); + + // Find audio track + v->audioTrack = FFMS_GetFirstTrackOfType(index, FFMS_TYPE_AUDIO, &v->errInfo); + if (v->audioTrack < 0) utilDie("%s", v->errInfo.Buffer); + v->audioSource = FFMS_CreateAudioSource(filename, v->audioTrack, index, FFMS_DELAY_FIRST_VIDEO_TRACK, &v->errInfo); + if (v->audioSource == NULL) utilDie("%s", v->errInfo.Buffer); + + // Get audio properties + v->audioProps = FFMS_GetAudioProperties(v->audioSource); + + // Index is now part of audioSource & videoSource, so release this one + FFMS_DestroyIndex(index); + + // Create video texture + SDL_SetHint(SDL_HINT_RENDER_SCALE_QUALITY, "linear"); + v->videoTexture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_BGRA32, SDL_TEXTUREACCESS_STATIC, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight); + if (v->videoTexture == NULL) utilDie("%s", SDL_GetError()); + + SDL_RenderSetLogicalSize(renderer, v->propFrame->EncodedWidth, v->propFrame->EncodedHeight); + + // Determine audio format + switch (v->audioProps->SampleFormat) { + case FFMS_FMT_U8: + v->audioFormat = AUDIO_U8; + v->audioSampleBytes = 1; + break; + case FFMS_FMT_S16: + v->audioFormat = AUDIO_S16SYS; + v->audioSampleBytes = 2; + break; + case FFMS_FMT_S32: + v->audioFormat = AUDIO_S32SYS; + v->audioSampleBytes = 4; + break; + case FFMS_FMT_FLT: + v->audioFormat = AUDIO_F32SYS; + v->audioSampleBytes = 4; + break; + default: + utilDie("Unknown audio sample format."); + break; + } + if (v->audioProps->Channels > 2) utilDie("Only mono and stereo audio are supported."); + + // Create audio stream to convert audio to our desired format + v->audioStream = SDL_NewAudioStream(v->audioFormat, (Uint8)v->audioProps->Channels, v->audioProps->SampleRate, _mixFormat, _mixChannels, _mixRate); + if (!v->audioStream) utilDie("%s", SDL_GetError()); + + // Create a buffer to read audio into before conversion + v->mixSampleSize = SDL_AUDIO_BITSIZE(_mixFormat) / 8 * _mixChannels; + v->audioSampleSize = v->audioSampleBytes * v->audioProps->Channels; + v->audioBufferSize = v->audioSampleSize * AUDIO_SAMPLE_PREREAD; + v->audioBuffer = (byte *)malloc((size_t)v->audioBufferSize * sizeof(byte)); + if (!v->audioBuffer) utilDie("Unable to allocate %ld byte audio buffer.", (size_t)v->audioBufferSize * sizeof(byte)); + + // Create a block of silent audio to overlay with video stream audio + v->audioSilenceSize = (Uint32)(_mixRate * SDL_AUDIO_BITSIZE(_mixFormat) / 8 * AUDIO_SILENCE_SECONDS); + v->audioSilenceRaw = (byte *)calloc(1, (size_t)v->audioSilenceSize * sizeof(byte)); + if (!v->audioSilenceRaw) utilDie("Unable to allocate %ld silence buffer.", v->audioSilenceSize); + + // Load silent audio + v->silenceChunk = Mix_QuickLoad_RAW(v->audioSilenceRaw, v->audioSilenceSize); + if (!v->silenceChunk) utilDie("%s", Mix_GetError()); + + // Start silent audio playback & immediately pause it + v->audioSilenceChannel = Mix_PlayChannel(-1, v->silenceChunk, -1); + if (v->audioSilenceChannel < 0) utilDie("%s", Mix_GetError()); + + // Register effect to provide video stream audio on this channel + Mix_RegisterEffect(v->audioSilenceChannel, _dequeueVideoAudio, NULL, v); + + /* + utilSay("Frames: %d (%dx%d) Audio Samples: %ld (%d Hz) %d Channel%s", + v->videoProps->NumFrames, + v->propFrame->EncodedWidth, + v->propFrame->EncodedHeight, + v->audioProps->NumSamples, + v->audioProps->SampleRate, + v->audioProps->Channels, + v->audioProps->Channels == 1 ? "" : "s" + ); + */ + + // Add to player hash + v->id = _nextId; + HASH_ADD_INT(_videoPlayerHash, id, v); + result = _nextId++; + + return result; +} + + +int videoPause(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoPause.", playerIndex); + + v->playing = false; + v->resetTime = true; + + return 0; +} + + +int videoPlay(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoPlay.", playerIndex); + + v->playing = true; + v->resetTime = true; + + return 0; +} + + +int videoQuit(void) { + + VideoPlayerT *v = NULL; + VideoPlayerT *t = NULL; + + // Unload any remaining videos + HASH_ITER(hh, _videoPlayerHash, v, t) { + videoUnload(v->id); + } + + FFMS_Deinit(); + + return 0; +} + + +int videoSeek(int playerIndex, int seekFrame) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoSeek.", playerIndex); + + while (seekFrame >= v->videoProps->NumFrames) { + seekFrame -= v->videoProps->NumFrames; + } + while (seekFrame < 0) { + seekFrame += v->videoProps->NumFrames; + } + + v->frame = seekFrame; + v->resetTime = true; + + return 0; +} + + +int videoUnload(int playerIndex) { + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoStop.", playerIndex); + + Mix_HaltChannel(v->audioSilenceChannel); + Mix_UnregisterEffect(v->audioSilenceChannel, _dequeueVideoAudio); + Mix_FreeChunk(v->silenceChunk); + free(v->audioSilenceRaw); + SDL_FreeAudioStream(v->audioStream); + free(v->audioBuffer); + + FFMS_DestroyAudioSource(v->audioSource); + FFMS_DestroyVideoSource(v->videoSource); + + SDL_DestroyTexture(v->videoTexture); + +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-align" + HASH_DEL(_videoPlayerHash, v); +#pragma GCC diagnostic pop + free(v); + + return 0; +} + + +int videoUpdate(int playerIndex, SDL_Texture **texture) { + int result = -1; + int64_t count = 0; + VideoPlayerT *v = NULL; + + // Get our player structure + HASH_FIND_INT(_videoPlayerHash, &playerIndex, v); + if (!v) utilDie("No video player at index %d in videoUpdate.", playerIndex); + + // Handle video frames (and time) + if ((SDL_GetTicks() - v->lastTickTime >= v->frameDeltaTime) || v->resetTime) { + v->lastTickTime = SDL_GetTicks(); + + if (v->frameData) { + SDL_UpdateTexture(v->videoTexture, NULL, v->frameData->Data[0], v->frameData->Linesize[0]); + } + + *texture = v->videoTexture; + result = v->frame; + + v->frameData = FFMS_GetFrame(v->videoSource, v->frame, &v->errInfo); + if (v->frameData == NULL) utilDie("%s", v->errInfo.Buffer); + v->frameInfo = FFMS_GetFrameInfo(FFMS_GetTrackFromVideo(v->videoSource), v->frame); + v->timestamp = (int64_t)((v->frameInfo->PTS * v->videoTimeBase->Num) / (double)v->videoTimeBase->Den); // Convert to milliseconds + v->frameDeltaTime = v->timestamp - v->lastFrameTime; + v->lastFrameTime = v->timestamp; + + if (v->playing) { + if (++v->frame >= v->videoProps->NumFrames) { + v->frame = 0; + v->timestamp = 0; + v->resetTime = true; + } + } + + if (v->resetTime) { + SDL_AudioStreamClear(v->audioStream); + v->lastTickTime = 0; + v->frameDeltaTime = 0; + v->audioPosition = (int64_t)((double)(v->timestamp * 0.001) * (double)v->audioProps->SampleRate); + v->resetTime = false; + } + } + + // Handle audio samples + if ((v->playing) && (SDL_AudioStreamAvailable(v->audioStream) < AUDIO_STREAM_LOW_WATERMARK) && (v->audioPosition < v->audioProps->NumSamples)) { + // Maximum samples we can read at a time + count = AUDIO_SAMPLE_PREREAD; + // Don't read past end of audio data + if (v->audioPosition + count >= v->audioProps->NumSamples) { + count = v->audioProps->NumSamples - v->audioPosition - 1; + } + // Are we reading anything? + if (count > 0) { + // Get audio from video stream + if (FFMS_GetAudio(v->audioSource, v->audioBuffer, v->audioPosition, count, &v->errInfo)) utilDie("%s", v->errInfo.Buffer); + // Feed it to the mixer stream + if (SDL_AudioStreamPut(v->audioStream, v->audioBuffer, (int)(count * v->audioSampleSize)) < 0) utilDie("%s", SDL_GetError()); + v->audioPosition += count; + } + } + + return result; +} diff --git a/singe/videoPlayer.h b/singe/videoPlayer.h new file mode 100644 index 000000000..38b240091 --- /dev/null +++ b/singe/videoPlayer.h @@ -0,0 +1,41 @@ +/* + * + * Singe 2 + * Copyright (C) 2019 Scott Duensing + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + */ + + +#ifndef VIDEOPLAYER_H +#define VIDEOPLAYER_H + + +#include + + +int videoInit(void); +int videoIsPlaying(int playerIndex); +int videoLoad(char *filename, SDL_Renderer *renderer); +int videoPause(int playerIndex); +int videoPlay(int playerIndex); +int videoQuit(void); +int videoSeek(int playerIndex, int seekFrame); +int videoUnload(int playerIndex); +int videoUpdate(int playerIndex, SDL_Texture **texture); + + +#endif // VIDEOPLAYER_H