├── .github └── workflows │ └── build_cmake.yml ├── .gitignore ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── VERSION.txt ├── audio ├── choc_AudioFileFormat.h ├── choc_AudioFileFormat_FLAC.h ├── choc_AudioFileFormat_MP3.h ├── choc_AudioFileFormat_Ogg.h ├── choc_AudioFileFormat_WAV.h ├── choc_AudioMIDIBlockDispatcher.h ├── choc_AudioSampleData.h ├── choc_MIDI.h ├── choc_MIDIFile.h ├── choc_MIDISequence.h ├── choc_Oscillators.h ├── choc_SampleBufferUtilities.h ├── choc_SampleBuffers.h ├── choc_SincInterpolator.h └── io │ ├── choc_AudioMIDIPlayer.h │ ├── choc_RenderingAudioMIDIPlayer.h │ ├── choc_RtAudioPlayer.h │ └── rtaudio │ ├── README.md │ ├── RtAudio.cpp │ ├── RtAudio.h │ ├── RtMidi.cpp │ └── RtMidi.h ├── containers ├── choc_ArgumentList.h ├── choc_COM.h ├── choc_DirtyList.h ├── choc_FIFOReadWritePosition.h ├── choc_MultipleReaderMultipleWriterFIFO.h ├── choc_NonAllocatingStableSort.h ├── choc_SingleReaderMultipleWriterFIFO.h ├── choc_SingleReaderSingleWriterFIFO.h ├── choc_SmallVector.h ├── choc_Span.h ├── choc_Value.h ├── choc_VariableSizeFIFO.h ├── choc_ZipFile.h └── choc_zlib.h ├── gui ├── choc_DesktopWindow.h ├── choc_MessageLoop.h └── choc_WebView.h ├── javascript ├── choc_javascript.h ├── choc_javascript_Console.h ├── choc_javascript_Duktape.h ├── choc_javascript_QuickJS.h ├── choc_javascript_Timer.h └── choc_javascript_V8.h ├── math └── choc_MathHelpers.h ├── memory ├── choc_AlignedMemoryBlock.h ├── choc_Base64.h ├── choc_Endianness.h ├── choc_ObjectPointer.h ├── choc_ObjectReference.h ├── choc_PoolAllocator.h ├── choc_VariableLengthEncoding.h └── choc_xxHash.h ├── network ├── choc_HTTPServer.h └── choc_MIMETypes.h ├── platform ├── choc_Assert.h ├── choc_BuildDate.h ├── choc_DetectDebugger.h ├── choc_DisableAllWarnings.h ├── choc_DynamicLibrary.h ├── choc_Execute.h ├── choc_FileWatcher.h ├── choc_HighResolutionSteadyClock.h ├── choc_MemoryDLL.h ├── choc_ObjectiveCHelpers.h ├── choc_Platform.h └── choc_ReenableAllWarnings.h ├── tests ├── CMakeLists.txt ├── choc_UnitTest.h ├── choc_tests.h ├── choc_webserver_example.h ├── choc_webview_example.h ├── main.cpp ├── main2.cpp └── test.zip ├── text ├── choc_CodePrinter.h ├── choc_Files.h ├── choc_FloatToString.h ├── choc_HTML.h ├── choc_JSON.h ├── choc_OpenSourceLicenseList.h ├── choc_StringUtilities.h ├── choc_TextTable.h ├── choc_UTF8.h └── choc_Wildcard.h ├── threading ├── choc_SpinLock.h ├── choc_TaskThread.h └── choc_ThreadSafeFunctor.h └── tidy_whitespace.py /.github/workflows/build_cmake.yml: -------------------------------------------------------------------------------- 1 | name: CHOC Unit Tests 2 | 3 | on: [push, pull_request] 4 | 5 | env: 6 | CMAKE_VERSION: 3.18.3 7 | BUILD_TYPE: Release 8 | 9 | jobs: 10 | build: 11 | name: ${{ matrix.config.name }} 12 | runs-on: ${{ matrix.config.os }} 13 | strategy: 14 | fail-fast: false 15 | matrix: 16 | config: 17 | - { 18 | name: "Windows Latest MSVC", 19 | os: windows-latest, 20 | cc: "cl", cxx: "cl", 21 | build_type: "Release", 22 | environment_script: "C:/Program Files (x86)/Microsoft Visual Studio/2019/Enterprise/VC/Auxiliary/Build/vcvars64.bat", 23 | generators: "Visual Studio 17 2022", 24 | exe: "./Release/choc_tests.exe" 25 | } 26 | - { 27 | name: "Windows Latest MinGW", 28 | os: windows-latest, 29 | build_type: "Release", 30 | cc: "gcc", cxx: "g++", 31 | generators: "Unix Makefiles", 32 | exe: "./choc_tests" 33 | } 34 | - { 35 | name: "Ubuntu Latest GCC", 36 | os: ubuntu-latest, 37 | build_type: "Release", 38 | cc: "gcc", cxx: "g++", 39 | generators: "Unix Makefiles", 40 | exe: "./choc_tests", 41 | install_packages: "sudo apt-get update; sudo apt-get -y install libgtk-3-dev libwebkit2gtk-4.1-dev libjack-dev libasound2-dev" 42 | } 43 | - { 44 | name: "macOS Latest Clang", 45 | os: macos-latest, 46 | build_type: "Release", 47 | cc: "clang", cxx: "clang++", 48 | generators: "Unix Makefiles", 49 | exe: "./choc_tests" 50 | } 51 | 52 | steps: 53 | - uses: actions/checkout@v1 54 | 55 | - name: Configure 56 | shell: bash 57 | working-directory: ./tests 58 | run: | 59 | ${{ matrix.config.install_packages }} 60 | mkdir build 61 | cd build 62 | cmake \ 63 | -S .. \ 64 | -B . \ 65 | -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} \ 66 | -G "${{ matrix.config.generators }}" 67 | 68 | - name: Build 69 | shell: bash 70 | working-directory: ./tests/build 71 | run: cmake --build . --config ${{ matrix.config.build_type }} 72 | 73 | - name: Run tests 74 | shell: bash 75 | working-directory: ./tests/build 76 | run: ${{ matrix.config.exe }} --multithread 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | ._* 3 | .vscode -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ## CHOC: contributing & CLA 2 | 3 | Apologies in advance, but thanks to several full-time jobs and a small child, I have no spare time to: 4 | 5 | - Create a suitable CLA (Contributor License Agreement) 6 | - Manage the process of getting contributors to sign it, and keeping legally-satisfactory records of that process (with all the GDPR implications that come along). 7 | - Spend time considering, reviewing and fixing-up PRs, and dealing diplomatically with poor-quality code, or needy contributors. 8 | 9 | So basically: please don't submit any PRs! 10 | 11 | **If you find a bug:** great! Please submit a bug report and I'll sort it out asap! 12 | **If you have a feature request:** that's also great.. please feel free to post it as an issue, but don't expect a quick response! 13 | 14 | Maybe in the future if there's a huge demand for it, then this situation will change, but in the short-term, I'm afraid you should treat the code here as a read-only resource! -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | ## CHOC: licensing 2 | 3 | I'd like anyone to feel able to use this library code without worrying about the legal implications, so it's released under the permissive ISC license: 4 | 5 | ---- 6 | 7 | **Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.** 8 | 9 | **THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.** 10 | 11 | https://en.wikipedia.org/wiki/ISC_license 12 | 13 | ---- 14 | 15 | Note that if you use the `choc::ui::WebView` class on Windows, it embeds some Microsoft redistributable code which has its own (permissive) license that you should be aware of. See inside the file `choc_WebView.h` for more details. 16 | -------------------------------------------------------------------------------- /VERSION.txt: -------------------------------------------------------------------------------- 1 | 1.0.0 -------------------------------------------------------------------------------- /audio/choc_MIDISequence.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_MIDISEQUENCE_HEADER_INCLUDED 20 | #define CHOC_MIDISEQUENCE_HEADER_INCLUDED 21 | 22 | #include "choc_MIDI.h" 23 | #include "../containers/choc_Span.h" 24 | #include "../containers/choc_NonAllocatingStableSort.h" 25 | 26 | namespace choc::midi 27 | { 28 | 29 | /// Contains a sequence of timed MIDI events, and provides iterators for them. 30 | struct Sequence 31 | { 32 | /// A time-stamped MIDI event 33 | struct Event 34 | { 35 | /// The units for this time-stamp are unspecified, so you can use it for seconds, 36 | /// frames, ticks, etc. as appropriate. 37 | double timeStamp; 38 | 39 | LongMessage message; 40 | 41 | bool operator< (const Event& other) const { return timeStamp < other.timeStamp; } 42 | }; 43 | 44 | /// The raw events in the sequence. Although this vector is public to allow access, 45 | /// the class expects the list to always remain sorted by time, and the timestamps 46 | /// must not be negative. 47 | std::vector events; 48 | 49 | /// If you've added events to the list, you can use this method to sort it by time. 50 | void sortEvents(); 51 | 52 | auto begin() const { return events.cbegin(); } 53 | auto end() const { return events.cend(); } 54 | 55 | auto begin() { return events.begin(); } 56 | auto end() { return events.end(); } 57 | 58 | /// An iterator for a choc::midi::Sequence. 59 | /// Note that if the sequence is modified while any iterators are active, 60 | /// their subsequent behaviour is undefined. 61 | struct Iterator 62 | { 63 | /// Creates an iterator positioned at the start of the sequence. 64 | Iterator (const Sequence&); 65 | Iterator (const Iterator&) = default; 66 | Iterator (Iterator&&) = default; 67 | 68 | /// Seeks the iterator to the given time 69 | void setTime (double newTime); 70 | 71 | /// Returns the current iterator time 72 | double getTime() const noexcept { return currentTime; } 73 | 74 | /// Returns a set of events which lie between the current time, up to (but not 75 | /// including) the given duration. This function then increments the iterator to 76 | /// set its current time to the end of this block. 77 | choc::span readNextEvents (double blockDuration); 78 | 79 | private: 80 | const Sequence& owner; 81 | double currentTime = 0; 82 | size_t nextIndex = 0; 83 | }; 84 | 85 | /// Returns an iterator for this sequence 86 | Iterator getIterator() const { return Iterator (*this); } 87 | }; 88 | 89 | 90 | //============================================================================== 91 | // _ _ _ _ 92 | // __| | ___ | |_ __ _ (_)| | ___ 93 | // / _` | / _ \| __| / _` || || |/ __| 94 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 95 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 96 | // 97 | // Code beyond this point is implementation detail... 98 | // 99 | //============================================================================== 100 | 101 | inline void Sequence::sortEvents() { choc::sorting::stable_sort (events.begin(), events.end()); } 102 | 103 | inline Sequence::Iterator::Iterator (const Sequence& s) : owner (s) {} 104 | 105 | inline void Sequence::Iterator::setTime (double newTimeStamp) 106 | { 107 | auto eventData = owner.events.data(); 108 | 109 | while (nextIndex != 0 && eventData[nextIndex - 1].timeStamp >= newTimeStamp) 110 | --nextIndex; 111 | 112 | while (nextIndex < owner.events.size() && eventData[nextIndex].timeStamp < newTimeStamp) 113 | ++nextIndex; 114 | 115 | currentTime = newTimeStamp; 116 | } 117 | 118 | inline choc::span Sequence::Iterator::readNextEvents (double duration) 119 | { 120 | auto start = nextIndex; 121 | auto eventData = owner.events.data(); 122 | auto end = start; 123 | auto total = owner.events.size(); 124 | auto endTime = currentTime + duration; 125 | currentTime = endTime; 126 | 127 | while (end < total && eventData[end].timeStamp < endTime) 128 | ++end; 129 | 130 | nextIndex = end; 131 | 132 | return { eventData + start, eventData + end }; 133 | } 134 | 135 | } // namespace choc::midi 136 | 137 | #endif // CHOC_MIDISEQUENCE_HEADER_INCLUDED 138 | -------------------------------------------------------------------------------- /audio/choc_SampleBufferUtilities.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_SAMPLE_BUFFER_UTILS_HEADER_INCLUDED 20 | #define CHOC_SAMPLE_BUFFER_UTILS_HEADER_INCLUDED 21 | 22 | #include "choc_SampleBuffers.h" 23 | #include "../containers/choc_Value.h" 24 | 25 | //============================================================================== 26 | namespace choc::buffer 27 | { 28 | 29 | /// Helper class which holds an InterleavedBuffer which it re-uses as intermediate 30 | /// storage when creating a temporary interleaved copy of a ChannelArrayView 31 | template 32 | struct InterleavingScratchBuffer 33 | { 34 | [[nodiscard]] InterleavedView getInterleavedBuffer (choc::buffer::Size size) 35 | { 36 | auto spaceNeeded = size.numChannels * size.numFrames; 37 | 38 | if (spaceNeeded > buffer.size()) 39 | buffer.resize (spaceNeeded); 40 | 41 | return createInterleavedView (buffer.data(), size.numChannels, size.numFrames); 42 | } 43 | 44 | template 45 | [[nodiscard]] InterleavedView interleave (const SourceBufferType& source) 46 | { 47 | auto dest = getInterleavedBuffer (source.getSize()); 48 | copy (dest, source); 49 | return dest; 50 | } 51 | 52 | private: 53 | std::vector buffer; 54 | }; 55 | 56 | /// Helper class which holds a ChannelArrayBuffer which it re-uses as intermediate 57 | /// storage when creating a temporary channel-based copy of an InterleavedView 58 | template 59 | struct DeinterleavingScratchBuffer 60 | { 61 | [[nodiscard]] ChannelArrayView getDeinterleavedBuffer (choc::buffer::Size size) 62 | { 63 | if (buffer.getNumChannels() < size.numChannels || buffer.getNumFrames() < size.numFrames) 64 | { 65 | buffer.resize (size); 66 | return buffer.getView(); 67 | } 68 | 69 | return buffer.getSection ({ 0, size.numChannels }, 70 | { 0, size.numFrames }); 71 | } 72 | 73 | template 74 | [[nodiscard]] ChannelArrayView deinterleave (const SourceBufferType& source) 75 | { 76 | auto dest = getDeinterleavedBuffer (source.getSize()); 77 | copy (dest, source); 78 | return dest; 79 | } 80 | 81 | private: 82 | ChannelArrayBuffer buffer; 83 | }; 84 | 85 | 86 | //============================================================================== 87 | /// Creates an InterleavedBufferView which points to the data in a choc::value::ValueView. 88 | /// The ValueView must be an array of either primitive values or vectors. 89 | /// @see createValueViewFromBuffer() 90 | template 91 | inline InterleavedView createInterleavedViewFromValue (const choc::value::ValueView& value) 92 | { 93 | auto& arrayType = value.getType(); 94 | CHOC_ASSERT (arrayType.isArray()); 95 | auto numFrames = arrayType.getNumElements(); 96 | auto sourceData = const_cast (reinterpret_cast (value.getRawData())); 97 | auto frameType = arrayType.getElementType(); 98 | 99 | if (frameType.isVector() || frameType.isUniformArray()) 100 | { 101 | CHOC_ASSERT (frameType.getElementType().isPrimitiveType()); 102 | return createInterleavedView (sourceData, frameType.getNumElements(), numFrames); 103 | } 104 | 105 | CHOC_ASSERT (frameType.isPrimitiveType()); 106 | return createInterleavedView (sourceData, 1, numFrames); 107 | } 108 | 109 | //============================================================================== 110 | /// Creates a ValueView for an array of vectors that represents the given 2D buffer. 111 | /// @see createInterleavedViewFromValue() 112 | template 113 | inline choc::value::ValueView createValueViewFromBuffer (const InterleavedView& source) 114 | { 115 | return choc::value::create2DArrayView (source.data.data, source.getNumFrames(), source.getNumChannels()); 116 | } 117 | 118 | 119 | } // namespace choc::buffer 120 | 121 | #endif 122 | -------------------------------------------------------------------------------- /audio/choc_SincInterpolator.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_SINC_INTERPOLATE_HEADER_INCLUDED 20 | #define CHOC_SINC_INTERPOLATE_HEADER_INCLUDED 21 | 22 | #include 23 | #include "choc_SampleBuffers.h" 24 | 25 | namespace choc::interpolation 26 | { 27 | 28 | /** 29 | Resamples the data from a choc::buffer::InterleavedView or choc::buffer::ChannelArrayView 30 | into a destination view, using a sinc interpolation algorithm. 31 | 32 | The source and destination buffers are expected to be different sizes, and the data will 33 | be resampled to fit the destination, which may involve up- or down-sampling depending on 34 | the sizes. 35 | 36 | The number of zero-crossings determines the quality, and larger numbers will be increasingly 37 | slow to calculate, with diminishing returns. A value of around 50 should be good enough for 38 | any normal purpose. 39 | */ 40 | template 41 | void sincInterpolate (DestBufferOrView&& destBuffer, const SourceBufferOrView& sourceBuffer) 42 | { 43 | using Sample = typename std::remove_reference::type::Sample; 44 | using MonoBufferView = choc::buffer::MonoView; 45 | 46 | constexpr auto floatZeroCrossings = static_cast (numZeroCrossings); 47 | constexpr auto floatPi = static_cast (3.141592653589793238); 48 | constexpr auto half = static_cast (0.5); 49 | constexpr auto one = static_cast (1); 50 | 51 | struct InterpolationFunctions 52 | { 53 | static void resampleMono (MonoBufferView destBuffer, MonoBufferView sourceBuffer) 54 | { 55 | auto destSize = destBuffer.getNumFrames(); 56 | auto sourceSize = sourceBuffer.getNumFrames(); 57 | 58 | if (destSize > sourceSize) 59 | { 60 | resampleMono (destBuffer, sourceBuffer, 1.0f); 61 | } 62 | else 63 | { 64 | choc::buffer::MonoBuffer bandlimitedIntermediate (1, sourceSize, false); 65 | resampleMono (bandlimitedIntermediate, sourceBuffer, static_cast (destSize) / static_cast (sourceSize)); 66 | resampleMono (destBuffer, bandlimitedIntermediate, 1.0f); 67 | } 68 | } 69 | 70 | static void resampleMono (MonoBufferView destBuffer, MonoBufferView sourceBuffer, float ratio) noexcept 71 | { 72 | CHOC_ASSERT (sourceBuffer.data.stride == 1); 73 | auto numFrames = destBuffer.getNumFrames(); 74 | double sourcePos = 0; 75 | auto sourceStride = sourceBuffer.getNumFrames() / static_cast (numFrames); 76 | auto dest = destBuffer.data.data; 77 | auto destStride = destBuffer.data.stride; 78 | 79 | for (decltype (numFrames) i = 0; i < numFrames; ++i) 80 | { 81 | *dest = calculateSample (sourceBuffer, sourcePos, ratio); 82 | dest += destStride; 83 | sourcePos += sourceStride; 84 | } 85 | } 86 | 87 | static Sample sincWindowFunction (Sample position) 88 | { 89 | return std::sin (position) 90 | * (half + half * std::cos (position * (one / floatZeroCrossings))) 91 | / position; 92 | } 93 | 94 | static Sample getSincWindowLevelAt (Sample position) 95 | { 96 | if (position == Sample()) 97 | return one; 98 | 99 | if (position < -floatZeroCrossings || position > floatZeroCrossings) 100 | return {}; 101 | 102 | return sincWindowFunction (position * floatPi); 103 | } 104 | 105 | static Sample calculateSample (MonoBufferView source, double position, float ratio) noexcept 106 | { 107 | auto sourcePosition = static_cast (position); 108 | auto fractionalOffset = static_cast (position - static_cast (sourcePosition)); 109 | 110 | if (fractionalOffset > 0) 111 | { 112 | fractionalOffset = one - fractionalOffset; 113 | ++sourcePosition; 114 | } 115 | 116 | auto data = source.data.data; 117 | auto numSourceFrames = source.getNumFrames(); 118 | 119 | Sample total = {}; 120 | auto numCrossings = static_cast (floatZeroCrossings / ratio); 121 | 122 | for (int i = -numCrossings; i <= numCrossings; ++i) 123 | { 124 | auto sourceIndex = static_cast (sourcePosition + i); 125 | 126 | if (sourceIndex < numSourceFrames) 127 | { 128 | auto windowPos = static_cast (fractionalOffset + (ratio * static_cast (i))); 129 | total += getSincWindowLevelAt (windowPos) * data[sourceIndex]; 130 | } 131 | } 132 | 133 | return total * ratio; 134 | } 135 | }; 136 | 137 | // The source and dest must have the same number of channels 138 | auto numChans = destBuffer.getNumChannels(); 139 | CHOC_ASSERT (sourceBuffer.getNumChannels() == numChans); 140 | 141 | if (! destBuffer.getSize().isEmpty()) 142 | { 143 | if (destBuffer.getNumFrames() != sourceBuffer.getNumFrames()) 144 | { 145 | for (decltype (numChans) i = 0; i < numChans; ++i) 146 | InterpolationFunctions::resampleMono (destBuffer.getChannel(i), sourceBuffer.getChannel(i)); 147 | } 148 | else 149 | { 150 | copy (destBuffer, sourceBuffer); 151 | } 152 | } 153 | } 154 | 155 | } // namespace choc::audio 156 | 157 | #endif 158 | -------------------------------------------------------------------------------- /audio/io/choc_RenderingAudioMIDIPlayer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_RENDERINGAUDIOMIDIPLAYER_HEADER_INCLUDED 20 | #define CHOC_RENDERINGAUDIOMIDIPLAYER_HEADER_INCLUDED 21 | 22 | #include 23 | #include "choc_AudioMIDIPlayer.h" 24 | 25 | namespace choc::audio::io 26 | { 27 | 28 | //============================================================================== 29 | /** 30 | * An AudioMIDIPlayer implementation that runs a fake audio device on a thread, 31 | * reading/writing its data via functions supplied by the caller. 32 | */ 33 | struct RenderingAudioMIDIPlayer : public AudioMIDIPlayer 34 | { 35 | /// You provide this function to generate blocks of input for the player. 36 | /// The background thread of RenderingAudioMIDIPlayer will repeatedly call 37 | /// this. It should return true if it wants to carry on, or false to cause 38 | /// the player to stop. 39 | using ProvideInputFn = std::function audioInput, 40 | std::vector& midiMessages, 41 | std::vector& midiMessageTimes)>; 42 | 43 | /// You provide this function to handle the blocks of output generated by the player. 44 | /// The background thread of RenderingAudioMIDIPlayer will repeatedly call 45 | /// this. It should return true if it wants to carry on, or false to cause 46 | /// the player to stop. 47 | using HandleOutputFn = std::function)>; 48 | 49 | RenderingAudioMIDIPlayer (const AudioDeviceOptions&, ProvideInputFn, HandleOutputFn); 50 | ~RenderingAudioMIDIPlayer() override; 51 | 52 | std::string getLastError() override { return {}; } 53 | std::vector getAvailableSampleRates() override { return {}; } 54 | std::vector getAvailableBlockSizes() override { return {}; } 55 | std::vector getAvailableAudioAPIs() override { return {}; } 56 | std::vector getAvailableInputDevices() override { return {}; } 57 | std::vector getAvailableOutputDevices() override { return {}; } 58 | std::vector getAvailableMIDIInputDevices() override { return {}; } 59 | std::vector getAvailableMIDIOutputDevices() override { return {}; } 60 | 61 | private: 62 | ProvideInputFn provideInput; 63 | HandleOutputFn handleOutput; 64 | std::thread renderThread; 65 | 66 | void start() override; 67 | void stop() override; 68 | void handleOutgoingMidiMessage (const void*, uint32_t) override {} 69 | void render(); 70 | }; 71 | 72 | 73 | 74 | 75 | //============================================================================== 76 | // _ _ _ _ 77 | // __| | ___ | |_ __ _ (_)| | ___ 78 | // / _` | / _ \| __| / _` || || |/ __| 79 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 80 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 81 | // 82 | // Code beyond this point is implementation detail... 83 | // 84 | //============================================================================== 85 | 86 | inline RenderingAudioMIDIPlayer::RenderingAudioMIDIPlayer (const AudioDeviceOptions& o, ProvideInputFn in, HandleOutputFn out) 87 | : AudioMIDIPlayer (o), provideInput (std::move (in)), handleOutput (std::move (out)) 88 | { 89 | // Because there are no restrictions on these values, they need to 90 | // be specified by the caller 91 | CHOC_ASSERT (options.blockSize != 0); 92 | CHOC_ASSERT (options.sampleRate != 0); 93 | } 94 | 95 | inline RenderingAudioMIDIPlayer::~RenderingAudioMIDIPlayer() 96 | { 97 | stop(); 98 | } 99 | 100 | inline void RenderingAudioMIDIPlayer::start() 101 | { 102 | renderThread = std::thread ([this] { render(); }); 103 | } 104 | 105 | inline void RenderingAudioMIDIPlayer::stop() 106 | { 107 | if (renderThread.joinable()) 108 | renderThread.join(); 109 | } 110 | 111 | inline void RenderingAudioMIDIPlayer::render() 112 | { 113 | choc::buffer::ChannelArrayBuffer audioInput (options.inputChannelCount, options.blockSize); 114 | choc::buffer::ChannelArrayBuffer audioOutput (options.outputChannelCount, options.blockSize); 115 | std::vector midiMessages; 116 | std::vector midiMessageTimes; 117 | midiMessages.reserve (512); 118 | midiMessageTimes.reserve (512); 119 | 120 | for (;;) 121 | { 122 | midiMessages.clear(); 123 | midiMessageTimes.clear(); 124 | 125 | { 126 | const std::scoped_lock lock (callbackLock); 127 | 128 | if (callbacks.empty()) 129 | return; 130 | } 131 | 132 | if (! provideInput (audioInput, midiMessages, midiMessageTimes)) 133 | return; 134 | 135 | CHOC_ASSERT (midiMessages.size() == midiMessageTimes.size()); 136 | 137 | if (auto totalNumMIDIMessages = static_cast (midiMessages.size())) 138 | { 139 | auto frameRange = audioOutput.getFrameRange(); 140 | uint32_t midiStart = 0; 141 | 142 | while (frameRange.start < frameRange.end) 143 | { 144 | auto chunkToDo = frameRange; 145 | auto endOfMIDI = midiStart; 146 | 147 | while (endOfMIDI < totalNumMIDIMessages) 148 | { 149 | auto eventTime = midiMessageTimes[endOfMIDI]; 150 | 151 | if (eventTime > chunkToDo.start) 152 | { 153 | chunkToDo.end = eventTime; 154 | break; 155 | } 156 | 157 | ++endOfMIDI; 158 | } 159 | 160 | for (uint32_t i = midiStart; i < endOfMIDI; ++i) 161 | addMIDIEvent ({}, midiMessages[i].data(), midiMessages[i].size()); 162 | 163 | process (audioInput.getFrameRange (chunkToDo), 164 | audioOutput.getFrameRange (chunkToDo), 165 | true); 166 | 167 | frameRange.start = chunkToDo.end; 168 | midiStart = endOfMIDI; 169 | } 170 | } 171 | else 172 | { 173 | process (audioInput, audioOutput, true); 174 | } 175 | 176 | if (! handleOutput (audioOutput)) 177 | return; 178 | } 179 | } 180 | 181 | } // namespace choc::audio::io 182 | 183 | #endif // CHOC_RENDERINGAUDIOMIDIPLAYER_HEADER_INCLUDED 184 | -------------------------------------------------------------------------------- /audio/io/rtaudio/README.md: -------------------------------------------------------------------------------- 1 | ## RtAudio 2 | 3 | This folder contains the RtAudio and RtMidi libraries. I could have written my own CHOC code to handle all the many audio and MIDI OS-specific APIs, but life is short, and RtAudio is permissively licensed.. 4 | 5 | Don't include these directly or add them to your build: the `choc_RtAudioPlayer.h` file includes them for you, so just include that and you're done. I've added some checks to these files to warn you if that happens by accident. 6 | 7 | -------------------------------------------------------------------------------- /containers/choc_DirtyList.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_DIRTY_LIST_HEADER_INCLUDED 20 | #define CHOC_DIRTY_LIST_HEADER_INCLUDED 21 | 22 | #include "choc_SingleReaderMultipleWriterFIFO.h" 23 | 24 | namespace choc::fifo 25 | { 26 | 27 | //============================================================================== 28 | /** 29 | A lock-free list of objects where multiple threads may mark an object as dirty, 30 | while a single thread polls the list and service the dirty ones. 31 | 32 | The main use-case for this class is where a set of objects need to be asynchronously 33 | serviced by a single thread or timer after they get flagged by a realtime thread. 34 | 35 | The class is designed so that the markAsDirty() and popNextDirtyObject() functions 36 | will execute in very short, constant time even when the total number of objects is 37 | very large, and no heap allocations or other system calls are involved. And if only 38 | one thread ever calls markAsDirty(), it's safe for realtime use. 39 | To make this possible, the compromises are that it needs to be initialised with a 40 | complete list of the objects needed, so that it can assign handles to them, and its 41 | memory requirements include allocating a small amount of storage per object. 42 | */ 43 | template 44 | struct DirtyList 45 | { 46 | DirtyList() = default; 47 | ~DirtyList() = default; 48 | 49 | DirtyList (DirtyList&&) = delete; 50 | DirtyList (const DirtyList&) = delete; 51 | 52 | using Handle = uint32_t; 53 | 54 | /// Prepares the list by giving it the complete set of objects that it will manage. 55 | /// The return value is the set of handles assigned to each of the objects. The handles 56 | /// are later needed by the caller in order to call markAsDirty(). 57 | /// 58 | /// Note that this method is not thread-safe, and must be performed before any other 59 | /// operations begin. It can be called multiple times to re-initialise the same list 60 | /// for other objects, as long as thread-safety is observed. 61 | template 62 | std::vector initialise (const Array& objects); 63 | 64 | /// Clears the queue of pending items and resets the 'dirty' state of all objects. 65 | void resetAll(); 66 | 67 | /// Marks an object as dirty. 68 | /// 69 | /// This may be called from any thread, and is lock-free. 70 | /// If the object is already marked as dirty, this function does nothing. If not, then 71 | /// the object is marked as dirty and added to the queue of objects which will later 72 | /// be returned by calls to popNextDirtyObject(). 73 | void markAsDirty (Handle objectHandle); 74 | 75 | /// Returns a pointer to the next dirty object (and in doing so marks that object 76 | /// as now being 'clean'). 77 | /// If no objects are dirty, this returns nullptr. 78 | /// This method is lock-free, but is designed to be called by only a single reader 79 | /// thread. 80 | ObjectType* popNextDirtyObject(); 81 | 82 | /// Returns true if any objects are currently queued for attention. 83 | bool areAnyObjectsDirty() const; 84 | 85 | private: 86 | //============================================================================== 87 | std::unique_ptr flags; // avoiding a vector here as atomics aren't copyable 88 | std::vector allObjects; 89 | choc::fifo::SingleReaderMultipleWriterFIFO fifo; 90 | }; 91 | 92 | 93 | 94 | //============================================================================== 95 | // _ _ _ _ 96 | // __| | ___ | |_ __ _ (_)| | ___ 97 | // / _` | / _ \| __| / _` || || |/ __| 98 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 99 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 100 | // 101 | // Code beyond this point is implementation detail... 102 | // 103 | //============================================================================== 104 | 105 | template 106 | template 107 | std::vector::Handle> DirtyList::initialise (const Array& objects) 108 | { 109 | std::vector handles; 110 | auto numObjects = static_cast (objects.size()); 111 | handles.reserve (numObjects); 112 | flags.reset (new std::atomic_flag[numObjects]); 113 | allObjects.resize (numObjects); 114 | fifo.reset (numObjects); 115 | size_t i = 0; 116 | 117 | for (auto& o : objects) 118 | { 119 | CHOC_ASSERT (o != nullptr); 120 | flags[i].clear(); 121 | allObjects[i] = o; 122 | handles.push_back (static_cast (i)); 123 | ++i; 124 | } 125 | 126 | return handles; 127 | } 128 | 129 | template 130 | void DirtyList::resetAll() 131 | { 132 | for (auto& o : allObjects) 133 | o.isDirty = false; 134 | 135 | fifo.reset(); 136 | } 137 | 138 | template 139 | void DirtyList::markAsDirty (Handle objectHandle) 140 | { 141 | CHOC_ASSERT (objectHandle < allObjects.size()); 142 | 143 | if (! flags[objectHandle].test_and_set()) 144 | fifo.push (objectHandle); 145 | } 146 | 147 | template 148 | ObjectType* DirtyList::popNextDirtyObject() 149 | { 150 | Handle item; 151 | 152 | if (! fifo.pop (item)) 153 | return nullptr; 154 | 155 | flags[item].clear(); 156 | return allObjects[item]; 157 | } 158 | 159 | template 160 | bool DirtyList::areAnyObjectsDirty() const 161 | { 162 | return fifo.getUsedSlots() != 0; 163 | } 164 | 165 | 166 | } // choc::fifo 167 | 168 | #endif 169 | -------------------------------------------------------------------------------- /containers/choc_FIFOReadWritePosition.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_FIFO_READ_WRITE_POSITION_HEADER_INCLUDED 20 | #define CHOC_FIFO_READ_WRITE_POSITION_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #undef max 26 | #undef min 27 | 28 | namespace choc::fifo 29 | { 30 | 31 | //============================================================================== 32 | /** 33 | Manages the read and write positions for a FIFO (but not the storage of 34 | objects in a FIFO). 35 | */ 36 | template 37 | struct FIFOReadWritePosition 38 | { 39 | FIFOReadWritePosition(); 40 | ~FIFOReadWritePosition() = default; 41 | 42 | //============================================================================== 43 | static constexpr IndexType invalidIndex = std::numeric_limits::max(); 44 | 45 | //============================================================================== 46 | /// Resets the positions and initialises the number of items in the FIFO. 47 | void reset (size_t numItems); 48 | 49 | /// Resets the FIFO positions, keeping the current size. 50 | void reset(); 51 | 52 | /// Returns the total number of items that the FIFO has been set up to hold. 53 | IndexType getTotalCapacity() const { return capacity; } 54 | /// Returns the number of items in the FIFO. 55 | IndexType getUsedSlots() const { return getUsed (readPos, writePos); } 56 | /// Returns the number of free slots in the FIFO. 57 | IndexType getFreeSlots() const { return getFree (readPos, writePos); } 58 | 59 | //============================================================================== 60 | struct WriteSlot 61 | { 62 | /// Returns true if a free slot was successfully obtained. 63 | operator bool() const { return index != invalidIndex; } 64 | 65 | /// The index of the slot that should be written to. 66 | IndexType index; 67 | 68 | private: 69 | friend struct FIFOReadWritePosition; 70 | IndexType newEnd; 71 | }; 72 | 73 | /// Attempts to get a slot into which the next item can be pushed. 74 | /// The WriteSlot object that is returned must be checked for validity by using its 75 | /// cast to bool operator - if the FIFO is full, it will be invalid. If it's valid 76 | /// then the caller must read what it needs from the slot at the index provided, and 77 | /// then immediately afterwards call unlock() to release the slot. 78 | WriteSlot lockSlotForWriting(); 79 | 80 | /// This must be called immediately after writing an item into the slot provided by 81 | /// lockSlotForWriting(). 82 | void unlock (WriteSlot); 83 | 84 | //============================================================================== 85 | struct ReadSlot 86 | { 87 | /// Returns true if a readable slot was successfully obtained. 88 | operator bool() const { return index != invalidIndex; } 89 | 90 | /// The index of the slot that should be read. 91 | IndexType index; 92 | 93 | private: 94 | friend struct FIFOReadWritePosition; 95 | IndexType newStart; 96 | }; 97 | 98 | /// Attempts to get a slot from which the first item can be read. 99 | /// The ReadSlot object that is returned must be checked for validity by using its 100 | /// cast to bool operator - if the FIFO is empty, it will be invalid. If it's valid 101 | /// then the caller must read what it needs from the slot at the index provided, and 102 | /// then immediately afterwards call unlock() to release the slot. 103 | ReadSlot lockSlotForReading(); 104 | 105 | /// This must be called immediately after reading an item from the slot provided by 106 | /// lockSlotForReading(). 107 | void unlock (ReadSlot); 108 | 109 | 110 | private: 111 | //============================================================================== 112 | uint32_t capacity = 0; 113 | AtomicType readPos, writePos; 114 | 115 | uint32_t getUsed (uint32_t s, uint32_t e) const { return e >= s ? (e - s) : (capacity + 1u - (s - e)); } 116 | uint32_t getFree (uint32_t s, uint32_t e) const { return e >= s ? (capacity + 1u - (e - s)) : (s - e); } 117 | uint32_t increment (uint32_t i) const { return i != capacity ? i + 1u : 0; } 118 | 119 | FIFOReadWritePosition (const FIFOReadWritePosition&) = delete; 120 | }; 121 | 122 | 123 | //============================================================================== 124 | // _ _ _ _ 125 | // __| | ___ | |_ __ _ (_)| | ___ 126 | // / _` | / _ \| __| / _` || || |/ __| 127 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 128 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 129 | // 130 | // Code beyond this point is implementation detail... 131 | // 132 | //============================================================================== 133 | 134 | template 135 | FIFOReadWritePosition::FIFOReadWritePosition() { reset (1); } 136 | 137 | template void FIFOReadWritePosition::reset (size_t size) 138 | { 139 | capacity = static_cast (size); 140 | reset(); 141 | } 142 | 143 | template void FIFOReadWritePosition::reset() 144 | { 145 | readPos = 0; 146 | writePos = 0; 147 | } 148 | 149 | template typename FIFOReadWritePosition::WriteSlot FIFOReadWritePosition::lockSlotForWriting() 150 | { 151 | WriteSlot slot; 152 | slot.index = writePos.load(); 153 | slot.newEnd = increment (slot.index); 154 | 155 | if (slot.newEnd == readPos) 156 | slot.index = invalidIndex; 157 | 158 | return slot; 159 | } 160 | 161 | template void FIFOReadWritePosition::unlock (WriteSlot slot) 162 | { 163 | writePos = slot.newEnd; 164 | } 165 | 166 | template typename FIFOReadWritePosition::ReadSlot FIFOReadWritePosition::lockSlotForReading() 167 | { 168 | ReadSlot slot; 169 | slot.index = readPos.load(); 170 | 171 | if (slot.index == writePos) 172 | { 173 | slot.index = invalidIndex; 174 | slot.newStart = slot.index; 175 | } 176 | else 177 | { 178 | slot.newStart = increment (slot.index); 179 | } 180 | 181 | return slot; 182 | } 183 | 184 | template void FIFOReadWritePosition::unlock (ReadSlot slot) 185 | { 186 | readPos = slot.newStart; 187 | } 188 | 189 | 190 | } // choc::fifo 191 | 192 | #endif 193 | -------------------------------------------------------------------------------- /containers/choc_MultipleReaderMultipleWriterFIFO.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_MULTI_READER_MULTI_WRITER_FIFO_HEADER_INCLUDED 20 | #define CHOC_MULTI_READER_MULTI_WRITER_FIFO_HEADER_INCLUDED 21 | 22 | #include "choc_SingleReaderMultipleWriterFIFO.h" 23 | 24 | namespace choc::fifo 25 | { 26 | 27 | //============================================================================== 28 | /** 29 | A simple atomic multiple-reader, multiple-writer FIFO. 30 | 31 | This does use some spin-locks, so it's not technically lock-free, although in 32 | practice it's very unlikely to cause any issues if used on a realtime thread. 33 | */ 34 | template 35 | struct MultipleReaderMultipleWriterFIFO 36 | { 37 | MultipleReaderMultipleWriterFIFO() = default; 38 | ~MultipleReaderMultipleWriterFIFO() = default; 39 | 40 | /// Clears the FIFO and allocates a size for it. 41 | /// Note that this is not thread-safe with respect to the other methods - it must 42 | /// only be called when nothing else is modifying the FIFO. 43 | void reset (size_t numItems) { fifo.reset (numItems); } 44 | 45 | /// Clears the FIFO and allocates a size for it, filling the slots with 46 | /// copies of the given object. 47 | void reset (size_t numItems, const Item& itemInitialiser) { fifo.reset (numItems, itemInitialiser); } 48 | 49 | /// Resets the FIFO, keeping the current size. 50 | void reset() { fifo.reset(); } 51 | 52 | /// Returns the number of items in the FIFO. 53 | uint32_t getUsedSlots() const { return fifo.getUsedSlots(); } 54 | /// Returns the number of free slots in the FIFO. 55 | uint32_t getFreeSlots() const { return fifo.getFreeSlots(); } 56 | 57 | /// Attempts to push an into into the FIFO, returning false if no space was available. 58 | bool push (const Item& item) { return fifo.push (item); } 59 | 60 | /// Attempts to push an into into the FIFO, returning false if no space was available. 61 | bool push (Item&& item) { return fifo.push (std::move (item)); } 62 | 63 | /// If any items are available, this copies the first into the given target, and returns true. 64 | bool pop (Item&); 65 | 66 | private: 67 | choc::fifo::SingleReaderMultipleWriterFIFO fifo; 68 | choc::threading::SpinLock readLock; 69 | 70 | MultipleReaderMultipleWriterFIFO (const MultipleReaderMultipleWriterFIFO&) = delete; 71 | }; 72 | 73 | 74 | //============================================================================== 75 | // _ _ _ _ 76 | // __| | ___ | |_ __ _ (_)| | ___ 77 | // / _` | / _ \| __| / _` || || |/ __| 78 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 79 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 80 | // 81 | // Code beyond this point is implementation detail... 82 | // 83 | //============================================================================== 84 | 85 | template bool MultipleReaderMultipleWriterFIFO::pop (Item& result) 86 | { 87 | const std::scoped_lock lock (readLock); 88 | return fifo.pop (result); 89 | } 90 | 91 | } // choc::fifo 92 | 93 | #endif 94 | -------------------------------------------------------------------------------- /containers/choc_SingleReaderMultipleWriterFIFO.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_SINGLE_READER_MULTI_WRITER_FIFO_HEADER_INCLUDED 20 | #define CHOC_SINGLE_READER_MULTI_WRITER_FIFO_HEADER_INCLUDED 21 | 22 | #include 23 | 24 | #include "choc_SingleReaderSingleWriterFIFO.h" 25 | #include "../threading/choc_SpinLock.h" 26 | 27 | namespace choc::fifo 28 | { 29 | 30 | //============================================================================== 31 | /** 32 | A simple atomic single-reader, multiple-writer FIFO. 33 | 34 | Note that the idea with this class is for it to be realtime-safe and lock-free 35 | for the reader, but the writers may very briefly block each other if more than 36 | one thread attempts to write at the same time. 37 | */ 38 | template 39 | struct SingleReaderMultipleWriterFIFO 40 | { 41 | SingleReaderMultipleWriterFIFO() = default; 42 | ~SingleReaderMultipleWriterFIFO() = default; 43 | 44 | /** Clears the FIFO and allocates a size for it. 45 | Note that this is not thread-safe with respect to the other methods - it must 46 | only be called when nothing else is modifying the FIFO. 47 | */ 48 | void reset (size_t numItems) { fifo.reset (numItems); } 49 | 50 | /** Clears the FIFO and allocates a size for it, filling the slots with 51 | copies of the given object. 52 | */ 53 | void reset (size_t numItems, const Item& itemInitialiser) { fifo.reset (numItems, itemInitialiser); } 54 | 55 | /** Resets the FIFO, keeping the current size. */ 56 | void reset() { fifo.reset(); } 57 | 58 | /** Returns the number of items in the FIFO. */ 59 | uint32_t getUsedSlots() const { return fifo.getUsedSlots(); } 60 | /** Returns the number of free slots in the FIFO. */ 61 | uint32_t getFreeSlots() const { return fifo.getFreeSlots(); } 62 | 63 | /** Attempts to push an into into the FIFO, returning false if no space was available. */ 64 | bool push (const Item&); 65 | 66 | /** Attempts to push an into into the FIFO, returning false if no space was available. */ 67 | bool push (Item&&); 68 | 69 | /** If any items are available, this copies the first into the given target, and returns true. */ 70 | bool pop (Item& result) { return fifo.pop (result); } 71 | 72 | private: 73 | choc::fifo::SingleReaderSingleWriterFIFO fifo; 74 | choc::threading::SpinLock writeLock; 75 | 76 | SingleReaderMultipleWriterFIFO (const SingleReaderMultipleWriterFIFO&) = delete; 77 | }; 78 | 79 | 80 | //============================================================================== 81 | // _ _ _ _ 82 | // __| | ___ | |_ __ _ (_)| | ___ 83 | // / _` | / _ \| __| / _` || || |/ __| 84 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 85 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 86 | // 87 | // Code beyond this point is implementation detail... 88 | // 89 | //============================================================================== 90 | 91 | template bool SingleReaderMultipleWriterFIFO::push (const Item& item) 92 | { 93 | const std::scoped_lock lock (writeLock); 94 | return fifo.push (item); 95 | } 96 | 97 | template bool SingleReaderMultipleWriterFIFO::push (Item&& item) 98 | { 99 | const std::scoped_lock lock (writeLock); 100 | return fifo.push (std::move (item)); 101 | } 102 | 103 | } // choc::fifo 104 | 105 | #endif 106 | -------------------------------------------------------------------------------- /containers/choc_SingleReaderSingleWriterFIFO.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_SINGLE_READER_WRITER_FIFO_HEADER_INCLUDED 20 | #define CHOC_SINGLE_READER_WRITER_FIFO_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #include "choc_FIFOReadWritePosition.h" 26 | 27 | namespace choc::fifo 28 | { 29 | 30 | //============================================================================== 31 | /** 32 | A simple atomic single-reader, single-writer FIFO. 33 | */ 34 | template 35 | struct SingleReaderSingleWriterFIFO 36 | { 37 | SingleReaderSingleWriterFIFO(); 38 | ~SingleReaderSingleWriterFIFO() = default; 39 | 40 | /** Clears the FIFO and allocates a size for it. */ 41 | void reset (size_t numItems); 42 | 43 | /** Clears the FIFO and allocates a size for it, filling the slots with 44 | copies of the given object. 45 | Note that this is not thread-safe with respect to the other methods - it must 46 | only be called when nothing else is modifying the FIFO. 47 | */ 48 | void reset (size_t numItems, const Item& itemInitialiser); 49 | 50 | /** Resets the FIFO, keeping the current size. */ 51 | void reset() { position.reset(); } 52 | 53 | /** Returns the number of items in the FIFO. */ 54 | uint32_t getUsedSlots() const { return position.getUsedSlots(); } 55 | /** Returns the number of free slots in the FIFO. */ 56 | uint32_t getFreeSlots() const { return position.getFreeSlots(); } 57 | 58 | /** Attempts to push an into into the FIFO, returning false if no space was available. */ 59 | bool push (const Item&); 60 | 61 | /** Attempts to push an into into the FIFO, returning false if no space was available. */ 62 | bool push (Item&&); 63 | 64 | /** If any items are available, this copies the first into the given target, and returns true. */ 65 | bool pop (Item& result); 66 | 67 | private: 68 | FIFOReadWritePosition> position; 69 | std::vector items; 70 | 71 | SingleReaderSingleWriterFIFO (const SingleReaderSingleWriterFIFO&) = delete; 72 | }; 73 | 74 | 75 | //============================================================================== 76 | // _ _ _ _ 77 | // __| | ___ | |_ __ _ (_)| | ___ 78 | // / _` | / _ \| __| / _` || || |/ __| 79 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 80 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 81 | // 82 | // Code beyond this point is implementation detail... 83 | // 84 | //============================================================================== 85 | 86 | template SingleReaderSingleWriterFIFO::SingleReaderSingleWriterFIFO() { reset (1); } 87 | 88 | template void SingleReaderSingleWriterFIFO::reset (size_t size) 89 | { 90 | position.reset (size); 91 | items.resize (size + 1u); 92 | } 93 | 94 | template void SingleReaderSingleWriterFIFO::reset (size_t size, const Item& itemToCopy) 95 | { 96 | position.reset (size); 97 | items.resize (size + 1u, itemToCopy); 98 | } 99 | 100 | template bool SingleReaderSingleWriterFIFO::push (const Item& item) 101 | { 102 | if (auto slot = position.lockSlotForWriting()) 103 | { 104 | items[slot.index] = item; 105 | position.unlock (slot); 106 | return true; 107 | } 108 | 109 | return false; 110 | } 111 | 112 | template bool SingleReaderSingleWriterFIFO::push (Item&& item) 113 | { 114 | if (auto slot = position.lockSlotForWriting()) 115 | { 116 | items[slot.index] = std::move (item); 117 | position.unlock (slot); 118 | return true; 119 | } 120 | 121 | return false; 122 | } 123 | 124 | template bool SingleReaderSingleWriterFIFO::pop (Item& result) 125 | { 126 | if (auto slot = position.lockSlotForReading()) 127 | { 128 | result = std::move (items[slot.index]); 129 | position.unlock (slot); 130 | return true; 131 | } 132 | 133 | return false; 134 | } 135 | 136 | 137 | } // choc::fifo 138 | 139 | #endif 140 | -------------------------------------------------------------------------------- /containers/choc_Span.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_SPAN_HEADER_INCLUDED 20 | #define CHOC_SPAN_HEADER_INCLUDED 21 | 22 | #include 23 | #include "../platform/choc_Assert.h" 24 | 25 | namespace choc 26 | { 27 | 28 | //============================================================================== 29 | /** This is a temporary stunt-double for std::span, with the intention of it being 30 | deprecated when there's more widespread compiler support for the real std::span. 31 | 32 | This class has fewer bells-and-whistles than a real std::span, but it does have 33 | the advantage of calling CHOC_ASSERT when mistakes are made like out-of-range 34 | accesses, which can be useful for getting clean error handling rather than UB. 35 | */ 36 | template 37 | struct span 38 | { 39 | span() = default; 40 | span (const span&) = default; 41 | span (span&&) = default; 42 | span& operator= (span&&) = default; 43 | span& operator= (const span&) = default; 44 | 45 | /// Construct from some raw start and end pointers. For an empty span, these 46 | /// can both be nullptr, but if one is a real pointer then the caller must ensure 47 | /// that start <= end. 48 | span (Item* start, Item* end) noexcept : s (start), e (end) {} 49 | 50 | /// Constructs a span from a pointer and length. 51 | /// The pointer must not be nullptr unless the length is 0. 52 | span (const Item* start, size_t length) noexcept : span (const_cast (start), const_cast (start) + length) {} 53 | 54 | /// Constructor taking a raw C++ array. 55 | template 56 | span (Item (&array)[length]) : span (array, length) {} 57 | 58 | /// Constructor which takes some kind of class like std::vector or std::array. 59 | /// Any class that provides data() and size() methods can be passed in. 60 | template 61 | span (const VectorOrArray& v) : span (v.data(), v.size()) {} 62 | 63 | /// Returns true if the span is empty. 64 | bool empty() const { return s == e; } 65 | 66 | /// Returns the number of elements. 67 | /// The length() and size() methods are equivalent. 68 | size_t size() const { return static_cast (e - s); } 69 | 70 | /// Returns the number of elements. 71 | /// The length() and size() methods are equivalent. 72 | size_t length() const { return static_cast (e - s); } 73 | 74 | /// Returns a raw pointer to the start of the data. 75 | Item* data() const noexcept { return s; } 76 | 77 | const Item& front() const { CHOC_ASSERT (! empty()); return *s; } 78 | const Item& back() const { CHOC_ASSERT (! empty()); return *(e - 1); } 79 | Item& front() { CHOC_ASSERT (! empty()); return *s; } 80 | Item& back() { CHOC_ASSERT (! empty()); return *(e - 1); } 81 | 82 | const Item& operator[] (size_t index) const { CHOC_ASSERT (index < length()); return s[index]; } 83 | Item& operator[] (size_t index) { CHOC_ASSERT (index < length()); return s[index]; } 84 | 85 | /// A handy bonus function for getting a (non-empty) span's tail elements 86 | span tail() const { CHOC_ASSERT (! empty()); return { s + 1, e }; } 87 | 88 | const Item* begin() const noexcept { return s; } 89 | const Item* end() const noexcept { return e; } 90 | Item* begin() noexcept { return s; } 91 | Item* end() noexcept { return e; } 92 | 93 | /// Helper function to return a std::vector copy of the span's elements. 94 | std::vector::type> createVector() const 95 | { 96 | return std::vector::type> (s, e); 97 | } 98 | 99 | /// Two spans are considered identical if their elements are all comparable 100 | template 101 | bool operator== (const OtherSpan& other) const 102 | { 103 | auto sz = size(); 104 | 105 | if (sz != other.size()) 106 | return false; 107 | 108 | for (decltype (sz) i = 0; i < sz; ++i) 109 | if (s[i] != other.s[i]) 110 | return false; 111 | 112 | return true; 113 | } 114 | 115 | template 116 | bool operator!= (const OtherSpan& other) const { return ! operator== (other); } 117 | 118 | private: 119 | Item* s = {}; 120 | Item* e = {}; 121 | }; 122 | 123 | } // namespace choc 124 | 125 | #endif // CHOC_SPAN_HEADER_INCLUDED 126 | -------------------------------------------------------------------------------- /javascript/choc_javascript_Console.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_JAVASCRIPT_CONSOLE_HEADER_INCLUDED 20 | #define CHOC_JAVASCRIPT_CONSOLE_HEADER_INCLUDED 21 | 22 | #include 23 | #include "choc_javascript.h" 24 | 25 | 26 | namespace choc::javascript 27 | { 28 | 29 | enum class LoggingLevel 30 | { 31 | log, info, warn, error, debug 32 | }; 33 | 34 | //============================================================================== 35 | /** This function will bind some standard console functions to a javascript 36 | context. 37 | 38 | Just call registerConsoleFunctions() on your choc::javascript::Context object 39 | and it will bind the appropriate functions for you to use. 40 | 41 | It provides a very stripped-down `console` object which has methods such as 42 | `log` and `debug` that will write to the functors you provide. This lets code 43 | call standard logging functions like `console.log()`. 44 | 45 | You can optionally provide your own functor to do the writing, or leave it 46 | empty to have the output written to std::cout and std::cerr. 47 | 48 | NB: this doesn't work with duktape: can't seem to get it to parse an object 49 | that has methods.. 50 | */ 51 | void registerConsoleFunctions (choc::javascript::Context&, 52 | std::function handleOutput = {}); 53 | 54 | 55 | 56 | 57 | //============================================================================== 58 | // _ _ _ _ 59 | // __| | ___ | |_ __ _ (_)| | ___ 60 | // / _` | / _ \| __| / _` || || |/ __| 61 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 62 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 63 | // 64 | // Code beyond this point is implementation detail... 65 | // 66 | //============================================================================== 67 | 68 | inline void registerConsoleFunctions (choc::javascript::Context& context, 69 | std::function handleOutput) 70 | { 71 | if (! handleOutput) 72 | { 73 | handleOutput = [] (std::string_view content, LoggingLevel level) 74 | { 75 | if (level == LoggingLevel::debug) 76 | std::cerr << content << std::endl; 77 | else 78 | std::cout << content << std::endl; 79 | }; 80 | } 81 | 82 | context.registerFunction ("_choc_console_log", 83 | [handleOutput] (ArgumentList args) mutable -> choc::value::Value 84 | { 85 | if (auto content = args[0]) 86 | { 87 | auto level = LoggingLevel::log; 88 | 89 | switch (args.get (1)) 90 | { 91 | case 0: level = LoggingLevel::log; break; 92 | case 1: level = LoggingLevel::info; break; 93 | case 2: level = LoggingLevel::warn; break; 94 | case 3: level = LoggingLevel::error; break; 95 | case 4: level = LoggingLevel::debug; break; 96 | } 97 | 98 | handleOutput (content->isString() ? content->toString() 99 | : choc::json::toString (*content), level); 100 | } 101 | 102 | return {}; 103 | }); 104 | 105 | context.run (R"( 106 | console = { 107 | log: function() { for (let a of arguments) _choc_console_log (a, 0); }, 108 | info: function() { for (let a of arguments) _choc_console_log (a, 1); }, 109 | warn: function() { for (let a of arguments) _choc_console_log (a, 2); }, 110 | error: function() { for (let a of arguments) _choc_console_log (a, 3); }, 111 | debug: function() { for (let a of arguments) _choc_console_log (a, 4); } 112 | }; 113 | )"); 114 | } 115 | 116 | } // namespace choc::javascript 117 | 118 | #endif // CHOC_JAVASCRIPT_CONSOLE_HEADER_INCLUDED 119 | -------------------------------------------------------------------------------- /javascript/choc_javascript_Timer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_JAVASCRIPT_TIMER_HEADER_INCLUDED 20 | #define CHOC_JAVASCRIPT_TIMER_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #include "choc_javascript.h" 26 | #include "../gui/choc_MessageLoop.h" 27 | 28 | 29 | namespace choc::javascript 30 | { 31 | 32 | //============================================================================== 33 | /** This function will bind some standard setInterval/setTimeout functions to 34 | a javascript engine. 35 | 36 | Just call registerTimerFunctions() on your choc::javascript::Context object 37 | and it will bind the appropriate functions for you to use. 38 | 39 | The timer implementation uses choc::messageloop::Timer, and the callbacks will 40 | be invoked on the message thread. So to work correctly, your app must be running 41 | a message loop, and you must be careful not to cause race conditions by calling 42 | into the same javascript context from other threads. 43 | 44 | The following functions are added, which should behave as you'd expect their 45 | standard javascript counterparts to work: 46 | 47 | setInterval (callback, milliseconds) // returns the new timer ID 48 | setTimeout (callback, milliseconds) // returns the new timer ID 49 | clearInterval (timerID) 50 | */ 51 | void registerTimerFunctions (choc::javascript::Context&); 52 | 53 | 54 | 55 | 56 | 57 | //============================================================================== 58 | // _ _ _ _ 59 | // __| | ___ | |_ __ _ (_)| | ___ 60 | // / _` | / _ \| __| / _` || || |/ __| 61 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 62 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 63 | // 64 | // Code beyond this point is implementation detail... 65 | // 66 | //============================================================================== 67 | 68 | inline void registerTimerFunctions (Context& context) 69 | { 70 | struct TimerList 71 | { 72 | TimerList (Context& c) : context (c) {} 73 | 74 | Context& context; 75 | std::unordered_map activeTimers; 76 | int64_t nextTimerID = 0; 77 | 78 | int64_t setTimeout (uint32_t interval) 79 | { 80 | auto timerID = ++nextTimerID; 81 | 82 | activeTimers[timerID] = choc::messageloop::Timer (interval, [this, timerID] 83 | { 84 | auto timeIDCopy = timerID; // local copy as this lambda will get deleted.. 85 | activeTimers.erase (timeIDCopy); 86 | 87 | try 88 | { 89 | context.invoke ("_choc_invokeTimeout", timeIDCopy); 90 | } 91 | catch (const choc::javascript::Error& e) 92 | { 93 | std::cerr << e.what() << std::endl; 94 | } 95 | 96 | return false; 97 | }); 98 | 99 | return timerID; 100 | } 101 | 102 | int64_t setInterval (uint32_t interval) 103 | { 104 | auto timerID = ++nextTimerID; 105 | 106 | activeTimers[timerID] = choc::messageloop::Timer (interval, [this, timerID] 107 | { 108 | try 109 | { 110 | context.invoke ("_choc_invokeInterval", timerID); 111 | } 112 | catch (const choc::javascript::Error& e) 113 | { 114 | std::cerr << e.what() << std::endl; 115 | } 116 | 117 | return true; 118 | }); 119 | 120 | return timerID; 121 | } 122 | 123 | void clearInterval (int64_t timerID) 124 | { 125 | activeTimers.erase (timerID); 126 | } 127 | }; 128 | 129 | auto timerList = std::make_shared (context); 130 | 131 | context.registerFunction ("_choc_setTimeout", [timerList] (ArgumentList args) -> choc::value::Value 132 | { 133 | if (auto interval = args.get (0); interval >= 0) 134 | return choc::value::Value (timerList->setTimeout (static_cast (interval))); 135 | 136 | return {}; 137 | }); 138 | 139 | context.registerFunction ("_choc_setInterval", [timerList] (ArgumentList args) -> choc::value::Value 140 | { 141 | if (auto interval = args.get (0); interval >= 0) 142 | return choc::value::Value (timerList->setInterval (static_cast (interval))); 143 | 144 | return {}; 145 | }); 146 | 147 | context.registerFunction ("_choc_clearInterval", [timerList] (ArgumentList args) -> choc::value::Value 148 | { 149 | if (auto timerID = args.get (0)) 150 | timerList->clearInterval (timerID); 151 | 152 | return {}; 153 | }); 154 | 155 | context.run (R"( 156 | var _choc_activeTimers = {}; 157 | 158 | function _choc_invokeTimeout (timerID) 159 | { 160 | var t = _choc_activeTimers[timerID]; 161 | _choc_activeTimers[timerID] = undefined; 162 | 163 | if (t) 164 | t(); 165 | } 166 | 167 | function _choc_invokeInterval (timerID) 168 | { 169 | var t = _choc_activeTimers[timerID]; 170 | 171 | if (t) 172 | t(); 173 | } 174 | 175 | function setInterval (callback, milliseconds) 176 | { 177 | var timerID = _choc_setInterval ((milliseconds >= 1 ? milliseconds : 1) | 0); 178 | 179 | if (timerID) 180 | _choc_activeTimers[timerID] = callback; 181 | 182 | return timerID; 183 | } 184 | 185 | function setTimeout (callback, milliseconds) 186 | { 187 | var timerID = _choc_setTimeout ((milliseconds >= 1 ? milliseconds : 1) | 0); 188 | 189 | if (timerID) 190 | _choc_activeTimers[timerID] = callback; 191 | 192 | return timerID; 193 | } 194 | 195 | function clearInterval (timerID) 196 | { 197 | _choc_activeTimers[timerID] = undefined; 198 | _choc_clearInterval (timerID | 0); 199 | } 200 | )"); 201 | } 202 | 203 | } // namespace choc::javascript 204 | 205 | #endif // CHOC_JAVASCRIPT_TIMER_HEADER_INCLUDED 206 | -------------------------------------------------------------------------------- /math/choc_MathHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_MATH_HELPERS_HEADER_INCLUDED 20 | #define CHOC_MATH_HELPERS_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | #ifdef _MSC_VER 26 | #include 27 | #pragma intrinsic (_BitScanReverse) 28 | 29 | #ifdef _WIN64 30 | #pragma intrinsic (_BitScanReverse64) 31 | #endif 32 | 33 | #if defined (_M_X64) && ! defined (_M_ARM64EC) 34 | #pragma intrinsic (_umul128) 35 | #define CHOC_HAS_UMUL128 1 36 | #endif 37 | #endif 38 | 39 | namespace choc::math 40 | { 41 | 42 | //============================================================================== 43 | /// Returns true if the given value is 2^something 44 | template 45 | constexpr bool isPowerOf2 (Integer n) { return n > 0 && (n & (n - 1)) == 0; } 46 | 47 | // Returns the number of contiguously-clear upper bits in a 32-bit value 48 | /// Note this operation is undefined for value == 0! 49 | inline uint32_t countUpperClearBits (uint32_t value) 50 | { 51 | #ifdef _MSC_VER 52 | unsigned long result = 0; 53 | _BitScanReverse (&result, static_cast (value)); 54 | return static_cast (31u - result); 55 | #else 56 | return static_cast (__builtin_clz (value)); 57 | #endif 58 | } 59 | 60 | /// Returns the number of contiguously-clear upper bits in a 64-bit value. 61 | /// Note this operation is undefined for value == 0! 62 | inline uint32_t countUpperClearBits (uint64_t value) 63 | { 64 | #ifdef _MSC_VER 65 | unsigned long result = 0; 66 | #ifdef _WIN64 67 | _BitScanReverse64 (&result, value); 68 | #else 69 | if (_BitScanReverse (&result, static_cast (value >> 32))) return static_cast (31u - result); 70 | _BitScanReverse (&result, static_cast (value)); 71 | #endif 72 | return static_cast (63u - result); 73 | #else 74 | return static_cast (__builtin_clzll (value)); 75 | #endif 76 | } 77 | 78 | /// Returns the number of decimal digits required to print a given unsigned number 79 | inline int getNumDecimalDigits (uint32_t n) 80 | { 81 | return n < 1000 ? (n < 10 ? 1 : (n < 100 ? 2 : 3)) 82 | : n < 1000000 ? (n < 10000 ? 4 : (n < 100000 ? 5 : 6)) 83 | : n < 100000000 ? (n < 10000000 ? 7 : 8) 84 | : n < 1000000000 ? 9 : 10; 85 | } 86 | 87 | 88 | //============================================================================== 89 | /// Used as a return type for multiply128() 90 | struct Int128 91 | { 92 | uint64_t high, low; 93 | }; 94 | 95 | /// A cross-platform function to multiply two 64-bit numbers and return a 128-bit result 96 | inline Int128 multiply128 (uint64_t a, uint64_t b) 97 | { 98 | #if CHOC_HAS_UMUL128 99 | Int128 result; 100 | result.low = _umul128 (a, b, &result.high); 101 | return result; 102 | #elif __LP64__ 103 | #if __GNUC__ 104 | #pragma GCC diagnostic push 105 | #pragma GCC diagnostic ignored "-Wpedantic" 106 | #endif 107 | auto total = static_cast (a) * static_cast (b); 108 | #if __GNUC__ 109 | #pragma GCC diagnostic pop 110 | #endif 111 | return { static_cast (total >> 64), static_cast (total) }; 112 | #else 113 | uint64_t a0 = static_cast (a), a1 = a >> 32, 114 | b0 = static_cast (b), b1 = b >> 32; 115 | auto p10 = a1 * b0, p00 = a0 * b0, 116 | p11 = a1 * b1, p01 = a0 * b1; 117 | auto middleBits = p10 + static_cast (p01) + (p00 >> 32); 118 | return { p11 + (middleBits >> 32) + (p01 >> 32), (middleBits << 32) | static_cast (p00) }; 119 | #endif 120 | } 121 | 122 | 123 | } // namespace choc::math 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /memory/choc_AlignedMemoryBlock.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_ALIGNEDMEMORYBLOCK_HEADER_INCLUDED 20 | #define CHOC_ALIGNEDMEMORYBLOCK_HEADER_INCLUDED 21 | 22 | #include 23 | 24 | namespace choc 25 | { 26 | 27 | /** 28 | A very simple container that manages a heap-allocated block of memory, ensuring 29 | that its address is aligned to the size provided in the class template argument. 30 | 31 | The size of the alignment must be a power-of-two. 32 | */ 33 | template 34 | struct AlignedMemoryBlock 35 | { 36 | AlignedMemoryBlock() = default; 37 | AlignedMemoryBlock (size_t initialSize); 38 | AlignedMemoryBlock (const AlignedMemoryBlock&); 39 | AlignedMemoryBlock (AlignedMemoryBlock&&) noexcept; 40 | ~AlignedMemoryBlock(); 41 | 42 | AlignedMemoryBlock& operator= (const AlignedMemoryBlock&); 43 | AlignedMemoryBlock& operator= (AlignedMemoryBlock&&) noexcept; 44 | 45 | /// Returns the aligned memory address, or nullptr if the block's size hasn't been set. 46 | void* data() noexcept { return alignedPointer; } 47 | /// Returns the available space in bytes. 48 | size_t size() const noexcept { return availableSize; } 49 | 50 | /// Re-initialises the memory with a new size. 51 | /// This method invalidates any currently allocated data, and the newly allocated 52 | /// block's contents will be in an undefined state. 53 | void resize (size_t newSize); 54 | 55 | /// Zeroes any allocated memory. 56 | void clear() noexcept; 57 | 58 | /// Releases any allocated memory. 59 | void reset(); 60 | 61 | private: 62 | void* alignedPointer = nullptr; 63 | char* allocatedData = nullptr; 64 | size_t availableSize = 0; 65 | }; 66 | 67 | 68 | //============================================================================== 69 | // _ _ _ _ 70 | // __| | ___ | |_ __ _ (_)| | ___ 71 | // / _` | / _ \| __| / _` || || |/ __| 72 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 73 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 74 | // 75 | // Code beyond this point is implementation detail... 76 | // 77 | //============================================================================== 78 | 79 | template 80 | AlignedMemoryBlock::AlignedMemoryBlock (size_t initialSize) 81 | { 82 | resize (initialSize); 83 | } 84 | 85 | template 86 | AlignedMemoryBlock::AlignedMemoryBlock (const AlignedMemoryBlock& other) 87 | { 88 | resize (other.availableSize); 89 | memcpy (alignedPointer, other.alignedPointer, availableSize); 90 | } 91 | 92 | template 93 | AlignedMemoryBlock::AlignedMemoryBlock (AlignedMemoryBlock&& other) noexcept 94 | : alignedPointer (other.alignedPointer), allocatedData (other.allocatedData), 95 | availableSize (other.availableSize) 96 | { 97 | other.alignedPointer = nullptr; 98 | other.allocatedData = nullptr; 99 | other.availableSize = 0; 100 | } 101 | 102 | template 103 | AlignedMemoryBlock& AlignedMemoryBlock::operator= (const AlignedMemoryBlock& other) 104 | { 105 | resize (other.availableSize); 106 | memcpy (alignedPointer, other.alignedPointer, availableSize); 107 | return *this; 108 | } 109 | 110 | template 111 | AlignedMemoryBlock& AlignedMemoryBlock::operator= (AlignedMemoryBlock&& other) noexcept 112 | { 113 | alignedPointer = other.alignedPointer; 114 | allocatedData = other.allocatedData; 115 | availableSize = other.availableSize; 116 | other.alignedPointer = nullptr; 117 | other.allocatedData = nullptr; 118 | other.availableSize = 0; 119 | return *this; 120 | } 121 | 122 | template 123 | AlignedMemoryBlock::~AlignedMemoryBlock() 124 | { 125 | static_assert (alignmentBytes > 0 && (alignmentBytes & (alignmentBytes - 1)) == 0, 126 | "choc::AlignedMemoryBlock requires the alignment value to be a power of 2"); 127 | 128 | reset(); 129 | } 130 | 131 | template 132 | void AlignedMemoryBlock::reset() 133 | { 134 | delete[] allocatedData; 135 | allocatedData = nullptr; 136 | alignedPointer = nullptr; 137 | availableSize = 0; 138 | } 139 | 140 | template 141 | void AlignedMemoryBlock::resize (size_t newSize) 142 | { 143 | if (newSize != size()) 144 | { 145 | reset(); 146 | 147 | if (newSize != 0) 148 | { 149 | availableSize = newSize; 150 | allocatedData = new char[newSize + alignmentBytes]; 151 | auto address = reinterpret_cast (allocatedData); 152 | alignedPointer = reinterpret_cast ((address + (alignmentBytes - 1u)) 153 | & ~static_cast (alignmentBytes - 1u)); 154 | } 155 | } 156 | } 157 | 158 | template 159 | void AlignedMemoryBlock::clear() noexcept 160 | { 161 | if (size() != 0) 162 | std::memset (data(), 0, size()); 163 | } 164 | 165 | } // namespace choc 166 | 167 | #endif // CHOC_ALIGNEDMEMORYBLOCK_HEADER_INCLUDED 168 | -------------------------------------------------------------------------------- /memory/choc_ObjectPointer.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_OBJECT_POINTER_HEADER_INCLUDED 20 | #define CHOC_OBJECT_POINTER_HEADER_INCLUDED 21 | 22 | #include 23 | 24 | namespace choc 25 | { 26 | 27 | //============================================================================== 28 | /** 29 | A very simple smart-pointer which simply wraps a raw pointer without owning it. 30 | 31 | Why use this rather than a raw pointer? Two main reasons: 32 | - Semantically, using this class makes it clear to the reader that the object is 33 | not being owned or managed by the code that has the pointer. 34 | - All dereferences of the smart-pointer will assert if the pointer is null. This 35 | means that if you make it a policy to always use this wrapper around your raw pointers, 36 | and also make CHOC_ASSERT throw an exception, then you can safely and cleanly capture 37 | all your null pointer errors with a handler, and log/report them rather than crashing. 38 | 39 | If you find yourself using one of these to hold pointer which can never be null, you should 40 | probably use choc::ObjectReference instead. 41 | 42 | @see choc::ObjectReference 43 | */ 44 | template 45 | struct ObjectPointer final 46 | { 47 | using ObjectType = Type; 48 | 49 | /// Creates a null ObjectPointer. 50 | ObjectPointer() = default; 51 | 52 | /// Creates a null ObjectPointer. 53 | ObjectPointer (decltype (nullptr)) noexcept {} 54 | 55 | /// Creates an ObjectPointer that points to a non-null source object. 56 | template ::value>::type> 57 | ObjectPointer (SourceObjectType& object) noexcept : pointer (std::addressof (object)) {} 58 | 59 | /// Creates an ObjectPointer from a raw pointer. 60 | template ::value>::type> 61 | explicit ObjectPointer (SourceObjectType* object) noexcept : pointer (object) {} 62 | 63 | /// Creates an ObjectPointer that points to another ObjectPointer (which may be of a 64 | /// different object type if it can be trivially cast to the target type). 65 | template ::value>::type> 66 | ObjectPointer (ObjectPointer object) noexcept : pointer (object.get()) {} 67 | 68 | /// Sets this ObjectPointer from another ObjectPointer (which may be of a different object type 69 | /// if it can be trivially cast to the target type). 70 | template ::value>::type> 71 | ObjectPointer& operator= (ObjectPointer newObject) noexcept { pointer = newObject.get(); return *this; } 72 | 73 | /// The dereference operator uses CHOC_ASSERT to check whether the pointer is null before returning it. 74 | ObjectType& operator*() const { CHOC_ASSERT (pointer != nullptr); return *pointer; } 75 | 76 | /// The dereference operator uses CHOC_ASSERT to check whether the pointer is null before returning it. 77 | ObjectType* operator->() const { CHOC_ASSERT (pointer != nullptr); return pointer; } 78 | 79 | /// Returns the raw pointer. 80 | ObjectType* get() const noexcept { return pointer; } 81 | 82 | /// Allows the pointer to be checked for nullness. 83 | operator bool() const noexcept { return pointer != nullptr; } 84 | 85 | /// Sets the pointer to null. 86 | void reset() noexcept { pointer = {}; } 87 | 88 | /// Sets the pointer to a new value. 89 | void reset (ObjectType* newPointer) noexcept { pointer = newPointer; } 90 | 91 | bool operator== (decltype (nullptr)) const noexcept { return pointer == nullptr; } 92 | bool operator!= (decltype (nullptr)) const noexcept { return pointer != nullptr; } 93 | 94 | template bool operator== (const OtherObjectType& other) const noexcept { return pointer == getPointer (other); } 95 | template bool operator!= (const OtherObjectType& other) const noexcept { return pointer != getPointer (other); } 96 | template bool operator< (const OtherObjectType& other) const noexcept { return pointer < getPointer (other); } 97 | 98 | private: 99 | ObjectType* pointer = {}; 100 | 101 | // These prevent accidental casts to pointers and numeric types via the bool() operator 102 | operator void*() const = delete; 103 | operator char() const = delete; 104 | operator short() const = delete; 105 | operator int() const = delete; 106 | operator long() const = delete; 107 | operator size_t() const = delete; 108 | 109 | template static auto getPointer (ObjectPointer o) noexcept { return o.pointer; } 110 | template static auto getPointer (const OtherType* o) noexcept { return o; } 111 | template static auto getPointer (const OtherType& o) noexcept { return std::addressof (o); } 112 | }; 113 | 114 | } // namespace choc 115 | 116 | #endif // CHOC_OBJECT_POINTER_HEADER_INCLUDED 117 | -------------------------------------------------------------------------------- /memory/choc_ObjectReference.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_OBJECT_REFERENCE_HEADER_INCLUDED 20 | #define CHOC_OBJECT_REFERENCE_HEADER_INCLUDED 21 | 22 | #include 23 | 24 | namespace choc 25 | { 26 | 27 | //============================================================================== 28 | /** 29 | A very simple smart-pointer which simply wraps a non-null reference to an object. 30 | 31 | This is similar to std::reference_wrapper, but is stricter about not allowing the 32 | creation of uninitialised references, which means that you can be pretty certain that 33 | you can't ever read or write a nullptr via this class. 34 | 35 | @see choc::ObjectPointer 36 | */ 37 | template 38 | struct ObjectReference final 39 | { 40 | using ObjectType = Type; 41 | 42 | /// An ObjectReference can only be created for a valid, non-null object reference. 43 | ObjectReference() = delete; 44 | 45 | /// An ObjectReference can never point to a nullptr! 46 | ObjectReference (decltype (nullptr)) = delete; 47 | 48 | template ::value>::type> 49 | ObjectReference (SourceObjectType& objectToReference) noexcept : target (std::addressof (objectToReference)) {} 50 | 51 | template ::value>::type> 52 | ObjectReference (ObjectReference objectToReference) noexcept : target (objectToReference.getPointer()) {} 53 | 54 | /// Allows the reference to be changed to a new non-null object. 55 | template ::value>::type> 56 | ObjectReference& operator= (SourceObjectType& newObject) noexcept { target = std::addressof (static_cast (newObject)); return *this; } 57 | 58 | operator ObjectType&() const noexcept { return *target; } 59 | ObjectType* operator->() const { return target; } 60 | 61 | ObjectType& get() const noexcept { return *target; } 62 | ObjectType* getPointer() const noexcept { return target; } 63 | ObjectType& getReference() const noexcept { return *target; } 64 | 65 | template bool operator== (const OtherObjectType& other) const noexcept { return target == getPointer (other); } 66 | template bool operator!= (const OtherObjectType& other) const noexcept { return target != getPointer (other); } 67 | template bool operator< (const OtherObjectType& other) const noexcept { return target < getPointer (other); } 68 | 69 | private: 70 | ObjectType* target; 71 | 72 | template static auto getPointer (ObjectReference o) noexcept { return o.target; } 73 | template static auto getPointer (const OtherType* o) noexcept { return o; } 74 | template static auto getPointer (const OtherType& o) noexcept { return std::addressof (o); } 75 | }; 76 | 77 | } // namespace choc 78 | 79 | #endif // CHOC_OBJECT_REFERENCE_HEADER_INCLUDED 80 | -------------------------------------------------------------------------------- /memory/choc_PoolAllocator.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_POOL_HEADER_INCLUDED 20 | #define CHOC_POOL_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "../platform/choc_Assert.h" 27 | 28 | namespace choc::memory 29 | { 30 | 31 | //============================================================================== 32 | /** 33 | A pool-based object allocator. 34 | 35 | A pool provides a way to quickly allocate objects whose lifetimes are tied to 36 | the lifetime of the pool rather than managed in the traditional ways. 37 | 38 | Calling Pool::allocate() will return a reference to a new object which will 39 | survive until the pool itself is later deleted or reset, at which point all 40 | the objects will be destroyed. 41 | 42 | Because all objects are allocated linearly from large heap blocks, allocation 43 | has very low overhead and is compact. This class also doesn't attempt to be 44 | thread-safe, which also helps to make it fast. 45 | 46 | Obviously a pool doesn't suit all use-cases, but in situations where you need 47 | to quickly allocate a large batch of objects and then use them for the lifetime of 48 | a finite task, it can be a very efficient tool. It's especially helpful if the group 49 | of objects have complicated ownership patterns which would make normal ownership 50 | techniques cumbersome. 51 | */ 52 | class Pool 53 | { 54 | public: 55 | Pool(); 56 | ~Pool() = default; 57 | 58 | Pool (Pool&&) = default; 59 | Pool (const Pool&) = delete; 60 | Pool& operator= (Pool&&) = default; 61 | Pool& operator= (const Pool&) = delete; 62 | 63 | /// Resets the pool, deleting all the objects that have been allocated. 64 | void reset(); 65 | 66 | /// Returns a reference to a newly-constructed object in the pool. 67 | /// The caller must not attempt to delete the object that is returned, it'll be 68 | /// automatically deleted when the pool itself is reset or deleted. 69 | template 70 | ObjectType& allocate (Args&&... constructorArgs); 71 | 72 | /// Allocates a chunk of raw memory 73 | void* allocateData (size_t numBytes); 74 | 75 | /// Stores a copy of the given string in the pool, and returns a view of it. 76 | /// The memory that it allocates is null-terminated, so the string_view can be 77 | /// cast to a const char* if you need one. 78 | std::string_view allocateString (std::string_view); 79 | 80 | 81 | private: 82 | //============================================================================== 83 | static constexpr const size_t itemAlignment = 16; 84 | static constexpr const size_t blockSize = 65536; 85 | 86 | static constexpr size_t alignSize (size_t n) { return (n + (itemAlignment - 1u)) & ~(itemAlignment - 1u); } 87 | 88 | struct Item; 89 | 90 | struct Block 91 | { 92 | Block (size_t); 93 | Block (Block&&); 94 | Block (const Block&) = delete; 95 | ~Block(); 96 | 97 | bool hasSpaceFor (size_t size) const noexcept; 98 | Item* getItem (size_t position) noexcept; 99 | Item& allocateItem (size_t); 100 | 101 | size_t nextItemOffset = 0, allocatedSize; 102 | std::unique_ptr space; 103 | }; 104 | 105 | std::vector blocks; 106 | void addBlock (size_t); 107 | Item& allocateItem (size_t); 108 | }; 109 | 110 | 111 | //============================================================================== 112 | // _ _ _ _ 113 | // __| | ___ | |_ __ _ (_)| | ___ 114 | // / _` | / _ \| __| / _` || || |/ __| 115 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 116 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 117 | // 118 | // Code beyond this point is implementation detail... 119 | // 120 | //============================================================================== 121 | 122 | inline Pool::Pool() { reset(); } 123 | 124 | inline void Pool::reset() 125 | { 126 | blocks.clear(); 127 | blocks.reserve (32); 128 | addBlock (blockSize); 129 | } 130 | 131 | struct Pool::Item 132 | { 133 | size_t size; 134 | using Destructor = void(void*); 135 | Destructor* destructor; 136 | 137 | static constexpr size_t getHeaderSize() { return alignSize (sizeof (Item)); } 138 | static constexpr size_t getSpaceNeeded (size_t content) { return alignSize (getHeaderSize() + content); } 139 | void* getItemData() noexcept { return reinterpret_cast (this) + getHeaderSize(); } 140 | }; 141 | 142 | inline Pool::Block::Block (size_t size) : allocatedSize (size), space (new char[size]) {} 143 | 144 | inline Pool::Block::Block (Block&& other) 145 | : nextItemOffset (other.nextItemOffset), 146 | allocatedSize (other.allocatedSize), 147 | space (std::move (other.space)) 148 | { 149 | other.nextItemOffset = 0; 150 | } 151 | 152 | inline Pool::Block::~Block() 153 | { 154 | for (size_t i = 0; i < nextItemOffset;) 155 | { 156 | auto item = getItem (i); 157 | 158 | if (item->destructor != nullptr) 159 | item->destructor (item->getItemData()); 160 | 161 | i += item->size; 162 | } 163 | } 164 | 165 | inline bool Pool::Block::hasSpaceFor (size_t size) const noexcept { return nextItemOffset + size <= allocatedSize; } 166 | inline Pool::Item* Pool::Block::getItem (size_t position) noexcept { return reinterpret_cast (space.get() + position); } 167 | 168 | inline Pool::Item& Pool::Block::allocateItem (size_t size) 169 | { 170 | auto i = getItem (nextItemOffset); 171 | i->size = size; 172 | i->destructor = nullptr; 173 | nextItemOffset += size; 174 | return *i; 175 | } 176 | 177 | inline void Pool::addBlock (size_t size) { blocks.push_back (Block (size)); } 178 | 179 | inline Pool::Item& Pool::allocateItem (size_t size) 180 | { 181 | if (! blocks.back().hasSpaceFor (size)) 182 | addBlock (std::max (blockSize, size)); 183 | 184 | return blocks.back().allocateItem (size); 185 | } 186 | 187 | inline void* Pool::allocateData (size_t size) 188 | { 189 | return allocateItem (Item::getSpaceNeeded (size)).getItemData(); 190 | } 191 | 192 | inline std::string_view Pool::allocateString (std::string_view s) 193 | { 194 | auto len = s.length(); 195 | auto space = static_cast (allocateData (len + 1)); 196 | std::memcpy (space, s.data(), len); 197 | space[len] = 0; 198 | return space; 199 | } 200 | 201 | template 202 | ObjectType& Pool::allocate (Args&&... constructorArgs) 203 | { 204 | static constexpr auto itemSize = Item::getSpaceNeeded (sizeof (ObjectType)); 205 | 206 | auto& newItem = allocateItem (itemSize); 207 | auto allocatedObject = new (newItem.getItemData()) ObjectType (std::forward (constructorArgs)...); 208 | 209 | if constexpr (! std::is_trivially_destructible::value) 210 | newItem.destructor = [] (void* t) { static_cast (t)->~ObjectType(); }; 211 | 212 | return *allocatedObject; 213 | } 214 | 215 | 216 | } // namespace choc::memory 217 | 218 | #endif // CHOC_POOL_HEADER_INCLUDED 219 | -------------------------------------------------------------------------------- /memory/choc_VariableLengthEncoding.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_VAR_LEN_INTS_HEADER_INCLUDED 20 | #define CHOC_VAR_LEN_INTS_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | /* 27 | This file contains some functions for encoding/decoding packed 28 | integer representations. Handy for serialisation tasks. 29 | */ 30 | 31 | namespace choc::integer_encoding 32 | { 33 | 34 | /// Writes an integer into a block of memory using a variable-length encoding system. 35 | /// This compression scheme encodes 7 bits per byte, using the upper bit as a flag to 36 | /// indicate that another byte follows. 37 | /// For speed, this function expects to always be given enough space to write into, so 38 | /// make sure that you provide at least (sizeof(IntegerType) * 7) / 8 + 1 bytes of space. 39 | /// Returns the number of bytes used. 40 | /// If you want to encode signed numbers, you may want to team this up with zigzagEncode() 41 | /// and zigzagDecode() to make it much more space-efficient for negative values. 42 | template 43 | size_t encodeVariableLengthInt (void* destBuffer, IntegerType valueToEncode); 44 | 45 | /// Decodes an integer from data that was produced by encodeVariableLengthInt(). 46 | /// This compression scheme encodes 7 bits per byte, using the upper bit as a flag to 47 | /// indicate that another byte follows. 48 | /// The bytesUsed parameter returns either a number of bytes that were needed, or 0 49 | /// if something went wrong. 50 | template 51 | IntegerType decodeVariableLengthInt (const void* encodedData, size_t availableSize, size_t& bytesUsed); 52 | 53 | /// Zigzag encodes an integer. 54 | /// Because negative numbers always have the high bit of an integer set, this means 55 | /// that if you try to variable-length encode them, they will always take more space 56 | /// than the original, uncompressed integer. Zigzag encoding is a trick that 57 | /// converts small negative numbers to positive values that avoid high bits, so when 58 | /// you're using variable-length encoding to store values which may be negative, it usually 59 | /// makes sense to call zigzagEncode() on your values before storing them, then call 60 | /// zigzagDecode() to get the original value back from the decompressed integer. 61 | template 62 | IntegerType zigzagEncode (IntegerType); 63 | 64 | /// Decodes an integer that was encoded with zigzagEncode(). 65 | /// See zigzagEncode() for details. 66 | template 67 | IntegerType zigzagDecode (IntegerType); 68 | 69 | 70 | //============================================================================== 71 | // _ _ _ _ 72 | // __| | ___ | |_ __ _ (_)| | ___ 73 | // / _` | / _ \| __| / _` || || |/ __| 74 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 75 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 76 | // 77 | // Code beyond this point is implementation detail... 78 | // 79 | //============================================================================== 80 | 81 | 82 | template 83 | size_t encodeVariableLengthInt (void* destBuffer, IntegerType valueToEncode) 84 | { 85 | size_t size = 0; 86 | auto n = static_cast::type> (valueToEncode); 87 | auto dest = static_cast (destBuffer); 88 | 89 | for (;;) 90 | { 91 | auto byte = static_cast (n & static_cast (127)); 92 | n >>= 7; 93 | 94 | if (n == 0) 95 | { 96 | dest[size] = byte; 97 | return size + 1; 98 | } 99 | 100 | dest[size++] = static_cast (byte | 0x80u); 101 | } 102 | } 103 | 104 | template 105 | IntegerType decodeVariableLengthInt (const void* encodedData, size_t availableSize, size_t& bytesUsed) 106 | { 107 | typename std::make_unsigned::type result = 0; 108 | auto source = static_cast (encodedData); 109 | size_t size = 0; 110 | 111 | for (uint32_t bit = 0;; bit += 7) 112 | { 113 | if (size == availableSize) 114 | { 115 | bytesUsed = 0; 116 | return 0; 117 | } 118 | 119 | auto byte = source[size++]; 120 | result |= (static_cast::type> (byte & 127) << bit); 121 | 122 | if (byte >> 7) 123 | continue; 124 | 125 | bytesUsed = size; 126 | return static_cast (result); 127 | } 128 | } 129 | 130 | template 131 | IntegerType zigzagEncode (IntegerType n) 132 | { 133 | auto un = static_cast::type> (n); 134 | auto sn = static_cast::type> (n); 135 | return static_cast ((sn >> (sizeof (IntegerType) * 8 - 1)) ^ static_cast (un << 1)); 136 | } 137 | 138 | template 139 | IntegerType zigzagDecode (IntegerType n) 140 | { 141 | auto un = static_cast::type> (n); 142 | auto sn = static_cast::type> (n); 143 | return static_cast (static_cast (un >> 1) ^ -(sn & static_cast (1))); 144 | } 145 | 146 | } // namespace choc::integer_encoding 147 | 148 | #endif // CHOC_VAR_LEN_INTS_HEADER_INCLUDED 149 | -------------------------------------------------------------------------------- /network/choc_MIMETypes.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_MIME_TYPES_HEADER_INCLUDED 20 | #define CHOC_MIME_TYPES_HEADER_INCLUDED 21 | 22 | #include "../text/choc_StringUtilities.h" 23 | 24 | 25 | namespace choc::network 26 | { 27 | /// Makes a guess at picking a sensible MIME type from a list of common ones, 28 | /// based on the extension of the filename/path/URL provided. If nothing matches, 29 | /// it will return `resultIfNotFound`. 30 | inline std::string getMIMETypeFromFilename (const std::string& filename, 31 | std::string_view resultIfNotFound = "application/text") 32 | { 33 | if (auto question = filename.rfind ("?"); question != std::string_view::npos) 34 | return getMIMETypeFromFilename (filename.substr (0, question)); 35 | 36 | auto getExtension = [&] () -> std::string 37 | { 38 | if (auto lastDot = filename.rfind ("."); lastDot != std::string_view::npos) 39 | return choc::text::trim (choc::text::toLowerCase (filename.substr (lastDot + 1))); 40 | 41 | return {}; 42 | }; 43 | 44 | if (auto extension = getExtension(); ! extension.empty()) 45 | { 46 | struct MIMEType { const char* ext; const char* mime; }; 47 | 48 | static constexpr const MIMEType types[] = 49 | { 50 | { "htm", "text/html" }, 51 | { "html", "text/html" }, 52 | { "css", "text/css" }, 53 | { "txt", "text/plain" }, 54 | { "md", "text/markdown" }, 55 | { "js", "application/javascript" }, 56 | { "wasm", "application/wasm" }, 57 | { "json", "application/json" }, 58 | { "xml", "application/xml" }, 59 | { "wav", "audio/wav" }, 60 | { "ogg", "audio/ogg" }, 61 | { "flac", "audio/flac" }, 62 | { "mid", "audio/mid" }, 63 | { "mp3", "audio/mpeg" }, 64 | { "mp4", "audio/mp4" }, 65 | { "aif", "audio/x-aiff" }, 66 | { "aiff", "audio/x-aiff" }, 67 | { "php", "text/html" }, 68 | { "flv", "video/x-flv" }, 69 | { "png", "image/png" }, 70 | { "jpe", "image/jpeg" }, 71 | { "jpeg", "image/jpeg" }, 72 | { "jpg", "image/jpeg" }, 73 | { "gif", "image/gif" }, 74 | { "bmp", "image/bmp" }, 75 | { "tiff", "image/tiff" }, 76 | { "tif", "image/tiff" }, 77 | { "ico", "image/vnd.microsoft.icon" }, 78 | { "svg", "image/svg+xml" }, 79 | { "svgz", "image/svg+xml" }, 80 | { "otf", "font/otf" }, 81 | { "sfnt", "font/sfnt" }, 82 | { "ttf", "font/ttf" }, 83 | { "woff", "font/woff" }, 84 | { "woff2", "font/woff2" } 85 | }; 86 | 87 | for (auto t : types) 88 | if (extension == t.ext) 89 | return t.mime; 90 | } 91 | 92 | return std::string (resultIfNotFound); 93 | } 94 | } 95 | 96 | 97 | #endif // CHOC_MIME_TYPES_HEADER_INCLUDED 98 | -------------------------------------------------------------------------------- /platform/choc_Assert.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_ASSERT_HEADER_INCLUDED 20 | #define CHOC_ASSERT_HEADER_INCLUDED 21 | 22 | // If the project doesn't define a custom implementation for CHOC_ASSERT, the default 23 | // behaviour is just to call the normal system assert() 24 | #ifndef CHOC_ASSERT 25 | #include 26 | #define CHOC_ASSERT(x) assert(x); 27 | #endif 28 | 29 | // It's never a smart idea to include any C headers before your C++ ones, as they 30 | // often pollute your namespace with all kinds of dangerous macros like these ones. 31 | // This file is included by many of the choc headers, so is a convenient place to 32 | // undef these. 33 | #undef max 34 | #undef min 35 | 36 | #endif // CHOC_ASSERT_HEADER_INCLUDED 37 | -------------------------------------------------------------------------------- /platform/choc_BuildDate.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_BUILDDATE_HEADER_INCLUDED 20 | #define CHOC_BUILDDATE_HEADER_INCLUDED 21 | 22 | #include 23 | 24 | namespace choc 25 | { 26 | /// Returns the date of the day on which this function was compiled, by 27 | /// parsing the __DATE__ macro into a std::chrono timepoint. 28 | constexpr std::chrono::system_clock::time_point getBuildDate() 29 | { 30 | using namespace std::chrono; 31 | 32 | constexpr month m (__DATE__[0] == 'J' ? (__DATE__[1] == 'a' ? 1u 33 | : (__DATE__[2] == 'n' ? 6u : 7u)) 34 | : __DATE__[0] == 'F' ? 2 35 | : __DATE__[0] == 'M' ? (__DATE__[2] == 'r' ? 3u : 5u) 36 | : __DATE__[0] == 'A' ? (__DATE__[1] == 'p' ? 4u : 8u) 37 | : __DATE__[0] == 'S' ? 9u 38 | : __DATE__[0] == 'O' ? 10u 39 | : __DATE__[0] == 'N' ? 11u 40 | : __DATE__[0] == 'D' ? 12u 41 | : 0u); 42 | 43 | constexpr day d ((__DATE__[4] == ' ') ? static_cast (__DATE__[5] - '0') 44 | : static_cast ((__DATE__[4] - '0') * 10 45 | + (__DATE__[5] - '0'))); 46 | 47 | constexpr year y ((__DATE__[7] - '0') * 1000 48 | + (__DATE__[8] - '0') * 100 49 | + (__DATE__[9] - '0') * 10 50 | + (__DATE__[10] - '0')); 51 | 52 | constexpr auto count = sys_days (year_month_day (y, m, d)); 53 | 54 | return time_point (count); 55 | } 56 | 57 | /// Returns the number of whole days that have elapsed since this 58 | /// function was compiled. 59 | /// @see getBuildDate() 60 | inline int getDaysSinceBuildDate() 61 | { 62 | auto buildDate = getBuildDate(); 63 | auto now = std::chrono::system_clock::now(); 64 | return static_cast (std::chrono::duration_cast (now - buildDate).count()); 65 | } 66 | } 67 | 68 | #endif // CHOC_BUILDDATE_HEADER_INCLUDED 69 | -------------------------------------------------------------------------------- /platform/choc_DetectDebugger.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_PLATFORM_DETECT_DEBUGGER_HEADER_INCLUDED 20 | #define CHOC_PLATFORM_DETECT_DEBUGGER_HEADER_INCLUDED 21 | 22 | #include "choc_Platform.h" 23 | 24 | namespace choc 25 | { 26 | 27 | /// Returns true if the current process is running in a debugger. 28 | inline bool isDebuggerActive(); 29 | 30 | } // namespace choc 31 | 32 | 33 | 34 | //============================================================================== 35 | // _ _ _ _ 36 | // __| | ___ | |_ __ _ (_)| | ___ 37 | // / _` | / _ \| __| / _` || || |/ __| 38 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 39 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 40 | // 41 | // Code beyond this point is implementation detail... 42 | // 43 | //============================================================================== 44 | 45 | #if CHOC_WINDOWS 46 | 47 | #include "choc_DynamicLibrary.h" 48 | #include "choc_Assert.h" 49 | 50 | inline bool choc::isDebuggerActive() 51 | { 52 | choc::file::DynamicLibrary kernelLib ("kernel32.dll"); 53 | using IsDebuggerPresentFn = int(__stdcall *)(); 54 | auto fn = reinterpret_cast (kernelLib.findFunction ("IsDebuggerPresent")); 55 | return fn != nullptr && fn() != 0; 56 | } 57 | 58 | //============================================================================== 59 | #elif CHOC_APPLE || CHOC_BSD 60 | 61 | #include 62 | #include 63 | 64 | inline bool choc::isDebuggerActive() 65 | { 66 | struct ::kinfo_proc result; 67 | size_t resultSize = sizeof (result); 68 | int params[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, ::getpid() }; 69 | ::sysctl (params, 4, &result, &resultSize, nullptr, 0); 70 | return (result.kp_proc.p_flag & P_TRACED) != 0; 71 | } 72 | 73 | //============================================================================== 74 | #elif CHOC_LINUX 75 | 76 | #include "../text/choc_Files.h" 77 | #include "../text/choc_StringUtilities.h" 78 | 79 | inline bool choc::isDebuggerActive() 80 | { 81 | auto readStatusFileItem = [] (std::string_view filename, std::string_view item) -> std::string 82 | { 83 | auto lines = choc::text::splitIntoLines (choc::file::loadFileAsString (std::string (filename)), false); 84 | 85 | for (auto i = lines.rbegin(); i != lines.rend(); ++i) 86 | { 87 | auto line = choc::text::trimStart (std::string_view (*i)); 88 | 89 | if (choc::text::startsWith (line, item)) 90 | { 91 | auto remainder = choc::text::trimStart (item.substr (item.length())); 92 | 93 | if (! remainder.empty() && remainder[0] == ':') 94 | return std::string (choc::text::trim (remainder.substr (1))); 95 | } 96 | } 97 | 98 | return {}; 99 | }; 100 | 101 | try 102 | { 103 | return std::stoi (readStatusFileItem ("/proc/self/status", "TracerPid")) > 0; 104 | } 105 | // an exception will mean that the file wasn't there, or the entry was missing 106 | // or malformed, so just shrug and assume it's not in the debugger 107 | catch (const std::exception&) {} 108 | 109 | return false; 110 | } 111 | 112 | #else 113 | #error "Unknown or unsupported OS!" 114 | #endif 115 | 116 | #endif // CHOC_PLATFORM_DETECT_DEBUGGER_HEADER_INCLUDED 117 | -------------------------------------------------------------------------------- /platform/choc_DisableAllWarnings.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | 20 | /* 21 | Sometimes you inevitably have to include some 3rd-party headers in your code, and it's 22 | depressing how often even the most widely-used libraries are full of poorly-written 23 | code that triggers all kinds of compiler warnings. 24 | 25 | Obviously you're a proper programmer who always has warnings turned up to full, and "-Werror" 26 | enabled, so rather than having to compromise your own standards to work around other people's 27 | laziness, this header provides an easy way to locally disable warnings by sandwiching your 28 | 3rd-party headers between includes of these two files, e.g: 29 | 30 | #include "choc_DisableAllWarnings.h" 31 | #include "SloppilyWrittenButEssentialLibraryCode.h" 32 | #include "choc_ReenableAllWarnings.h" 33 | */ 34 | 35 | #if __clang__ 36 | #pragma clang diagnostic push 37 | #pragma clang diagnostic ignored "-Weverything" 38 | #elif __GNUC__ 39 | #pragma GCC diagnostic push 40 | #pragma GCC diagnostic ignored "-Wall" 41 | #pragma GCC diagnostic ignored "-Wpragmas" 42 | #pragma GCC diagnostic ignored "-Wextra" 43 | #pragma GCC diagnostic ignored "-Wshadow" 44 | #pragma GCC diagnostic ignored "-Wunused-parameter" 45 | #pragma GCC diagnostic ignored "-Wconversion" 46 | #pragma GCC diagnostic ignored "-Wsign-conversion" 47 | #pragma GCC diagnostic ignored "-Wsign-compare" 48 | #pragma GCC diagnostic ignored "-Wfloat-conversion" 49 | #pragma GCC diagnostic ignored "-Wswitch-enum" 50 | #pragma GCC diagnostic ignored "-Wswitch" 51 | #pragma GCC diagnostic ignored "-Wzero-as-null-pointer-constant" 52 | #pragma GCC diagnostic ignored "-Wunused-variable" 53 | #pragma GCC diagnostic ignored "-Wredundant-decls" 54 | #pragma GCC diagnostic ignored "-Wsubobject-linkage" 55 | #pragma GCC diagnostic ignored "-Wunused-but-set-variable" 56 | #pragma GCC diagnostic ignored "-Wredundant-move" 57 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" 58 | #pragma GCC diagnostic ignored "-Woverloaded-virtual" 59 | #pragma GCC diagnostic ignored "-Wc99-extensions" 60 | #pragma GCC diagnostic ignored "-Wmisleading-indentation" 61 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized" 62 | #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" 63 | #pragma GCC diagnostic ignored "-Wcast-function-type" 64 | #pragma GCC diagnostic ignored "-Wunused-label" 65 | #pragma GCC diagnostic ignored "-Wnarrowing" 66 | #pragma GCC diagnostic ignored "-Wparentheses" 67 | #pragma GCC diagnostic ignored "-Wwrite-strings" 68 | #pragma GCC diagnostic ignored "-Wformat-overflow" 69 | #pragma GCC diagnostic ignored "-Wdeprecated-copy-with-dtor" 70 | #pragma GCC diagnostic ignored "-Wunused-but-set-variable" 71 | #pragma GCC diagnostic ignored "-Wdeprecated" 72 | #pragma GCC diagnostic ignored "-Wdeprecated-declarations" 73 | #pragma GCC diagnostic ignored "-Wuse-after-free" 74 | #pragma GCC diagnostic ignored "-Warray-bounds" 75 | #pragma GCC diagnostic ignored "-Wvolatile" 76 | #pragma GCC diagnostic ignored "-Wmissing-field-initializers" 77 | #pragma GCC diagnostic ignored "-Wfloat-equal" 78 | #pragma GCC diagnostic ignored "-Wpedantic" 79 | #pragma GCC diagnostic ignored "-Wvla" 80 | #ifndef __MINGW32__ 81 | #pragma GCC diagnostic ignored "-Wredundant-move" 82 | #endif 83 | #else 84 | #pragma warning (push, 0) 85 | #pragma warning (disable: 2440) 86 | #pragma warning (disable: 2664) 87 | #pragma warning (disable: 4244) 88 | #pragma warning (disable: 4701) 89 | #pragma warning (disable: 4702) 90 | #pragma warning (disable: 4706) 91 | #pragma warning (disable: 4722) 92 | #pragma warning (disable: 6011) 93 | #pragma warning (disable: 6246) 94 | #pragma warning (disable: 6255) 95 | #pragma warning (disable: 6262) 96 | #pragma warning (disable: 6297) 97 | #pragma warning (disable: 6308) 98 | #pragma warning (disable: 6323) 99 | #pragma warning (disable: 6340) 100 | #pragma warning (disable: 6385) 101 | #pragma warning (disable: 6386) 102 | #pragma warning (disable: 28182) 103 | #endif 104 | -------------------------------------------------------------------------------- /platform/choc_DynamicLibrary.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED 20 | #define CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED 21 | 22 | #include 23 | 24 | namespace choc::file 25 | { 26 | 27 | //============================================================================== 28 | /** 29 | A minimal cross-platform loader for .dll/.so files. 30 | */ 31 | struct DynamicLibrary 32 | { 33 | DynamicLibrary() = default; 34 | 35 | /// Attempts to load a library with the given name or path. 36 | DynamicLibrary (std::string_view library); 37 | 38 | DynamicLibrary (const DynamicLibrary&) = delete; 39 | DynamicLibrary& operator= (const DynamicLibrary&) = delete; 40 | DynamicLibrary (DynamicLibrary&&); 41 | DynamicLibrary& operator= (DynamicLibrary&&); 42 | 43 | /// On destruction, this object releases the library that was loaded 44 | ~DynamicLibrary(); 45 | 46 | /// Returns a pointer to the function with this name, or nullptr if not found. 47 | void* findFunction (std::string_view functionName); 48 | 49 | /// Returns true if the library was successfully loaded 50 | operator bool() const noexcept { return handle != nullptr; } 51 | 52 | /// Releases any handle that this object is holding 53 | void close(); 54 | 55 | /// platform-specific handle. Will be nullptr if not loaded 56 | void* handle = nullptr; 57 | }; 58 | 59 | 60 | 61 | //============================================================================== 62 | // _ _ _ _ 63 | // __| | ___ | |_ __ _ (_)| | ___ 64 | // / _` | / _ \| __| / _` || || |/ __| 65 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 66 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 67 | // 68 | // Code beyond this point is implementation detail... 69 | // 70 | //============================================================================== 71 | 72 | inline DynamicLibrary::~DynamicLibrary() 73 | { 74 | close(); 75 | } 76 | 77 | inline DynamicLibrary::DynamicLibrary (DynamicLibrary&& other) : handle (other.handle) 78 | { 79 | other.handle = nullptr; 80 | } 81 | 82 | inline DynamicLibrary& DynamicLibrary::operator= (DynamicLibrary&& other) 83 | { 84 | close(); 85 | handle = other.handle; 86 | other.handle = nullptr; 87 | return *this; 88 | } 89 | 90 | } // namespace choc::file 91 | 92 | #if ! (defined (_WIN32) || defined (_WIN64)) 93 | 94 | #include 95 | 96 | inline choc::file::DynamicLibrary::DynamicLibrary (std::string_view library) 97 | { 98 | handle = ::dlopen (std::string (library).c_str(), RTLD_LOCAL | RTLD_NOW); 99 | } 100 | 101 | inline void choc::file::DynamicLibrary::close() 102 | { 103 | if (handle != nullptr) 104 | { 105 | ::dlclose (handle); 106 | handle = nullptr; 107 | } 108 | } 109 | 110 | inline void* choc::file::DynamicLibrary::findFunction (std::string_view name) 111 | { 112 | if (handle != nullptr) 113 | return ::dlsym (handle, std::string (name).c_str()); 114 | 115 | return {}; 116 | } 117 | 118 | #else 119 | 120 | //============================================================================== 121 | namespace choc::win32_defs 122 | { 123 | #if ! (defined (_WINDOWS_) || defined (_APISETLIBLOADER_)) // only use these local definitions if windows.h isn't already included 124 | using CHOC_HMODULE = void*; 125 | #ifdef _MSC_VER 126 | extern "C" CHOC_HMODULE __stdcall choc_LoadLibraryA (const char*); 127 | extern "C" int __stdcall choc_FreeLibrary (CHOC_HMODULE); 128 | extern "C" void* __stdcall choc_GetProcAddress (CHOC_HMODULE, const char*); 129 | #pragma comment(linker,"/alternatename:choc_LoadLibraryA=LoadLibraryA") 130 | #pragma comment(linker,"/alternatename:choc_FreeLibrary=FreeLibrary") 131 | #pragma comment(linker,"/alternatename:choc_GetProcAddress=GetProcAddress") 132 | static inline CHOC_HMODULE LoadLibraryA (const char* l) { return choc_LoadLibraryA (l); } 133 | static inline int FreeLibrary (CHOC_HMODULE m) { return choc_FreeLibrary (m); } 134 | static inline void* GetProcAddress (CHOC_HMODULE m, const char* f) { return choc_GetProcAddress (m, f); } 135 | #else 136 | extern "C" __declspec(dllimport) CHOC_HMODULE __stdcall LoadLibraryA (const char*); 137 | extern "C" __declspec(dllimport) int __stdcall FreeLibrary (CHOC_HMODULE); 138 | extern "C" __declspec(dllimport) void* __stdcall GetProcAddress (CHOC_HMODULE, const char*); 139 | #endif 140 | #else 141 | using CHOC_HMODULE = HMODULE; 142 | static inline CHOC_HMODULE LoadLibraryA (const char* l) { return ::LoadLibraryA (l); } 143 | static inline int FreeLibrary (CHOC_HMODULE m) { return ::FreeLibrary (m); } 144 | static inline void* GetProcAddress (CHOC_HMODULE m, const char* f) { return (void*) ::GetProcAddress (m, f); } 145 | #endif 146 | } 147 | 148 | inline choc::file::DynamicLibrary::DynamicLibrary (std::string_view library) 149 | { 150 | handle = (void*) win32_defs::LoadLibraryA (std::string (library).c_str()); 151 | } 152 | 153 | inline void choc::file::DynamicLibrary::close() 154 | { 155 | if (handle != nullptr) 156 | { 157 | win32_defs::FreeLibrary ((win32_defs::CHOC_HMODULE) handle); 158 | handle = nullptr; 159 | } 160 | } 161 | 162 | inline void* choc::file::DynamicLibrary::findFunction (std::string_view name) 163 | { 164 | if (handle != nullptr) 165 | return (void*) win32_defs::GetProcAddress ((win32_defs::CHOC_HMODULE) handle, std::string (name).c_str()); 166 | 167 | return {}; 168 | } 169 | 170 | #endif 171 | 172 | #endif // CHOC_DYNAMIC_LIBRARY_HEADER_INCLUDED 173 | -------------------------------------------------------------------------------- /platform/choc_Execute.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_EXECUTE_HEADER_INCLUDED 20 | #define CHOC_EXECUTE_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | 26 | namespace choc 27 | { 28 | 29 | /// The result of a call to the execute() function. 30 | struct ProcessResult 31 | { 32 | /// The process's output 33 | std::string output; 34 | 35 | /// The process's status code on exit. This is a standard unix-style 36 | /// exit code as returned, where 0 is success, and other values 37 | /// indicate failures. 38 | int statusCode = 0; 39 | }; 40 | 41 | //============================================================================== 42 | /// Executes a system command synchronously, returning when the spawned process 43 | /// has terminated, and providing the command's output as a string, along with 44 | /// the process's status code. 45 | /// 46 | /// @param command The command to attempt to run. 47 | /// @param alsoReadStdErr If set to true, the stderr stream will also be read and 48 | /// returned as part of the output. If false, just stdout will be read. 49 | /// 50 | /// This may throw an exception if something fails. 51 | /// 52 | ProcessResult execute (std::string command, 53 | bool alsoReadStdErr = false); 54 | 55 | 56 | 57 | 58 | 59 | //============================================================================== 60 | // _ _ _ _ 61 | // __| | ___ | |_ __ _ (_)| | ___ 62 | // / _` | / _ \| __| / _` || || |/ __| 63 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 64 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 65 | // 66 | // Code beyond this point is implementation detail... 67 | // 68 | //============================================================================== 69 | 70 | 71 | static std::string getErrnoString() 72 | { 73 | char errorText[256] = {}; 74 | 75 | #ifdef _WIN32 76 | strerror_s (errorText, sizeof(errorText) - 1, errno); 77 | return errorText; 78 | #else 79 | struct Helper 80 | { 81 | // overloads to handle two variants of strerror_r that return different things 82 | static const char* handleResult (int, const char* s) { return s; } 83 | static const char* handleResult (const char* s, const char*) { return s; } 84 | }; 85 | 86 | return Helper::handleResult (strerror_r (errno, errorText, sizeof(errorText)), errorText); 87 | #endif 88 | } 89 | 90 | inline ProcessResult execute (std::string command, bool alsoReadStdErr) 91 | { 92 | if (alsoReadStdErr) 93 | command += " 2>&1"; 94 | 95 | #ifdef _MSC_VER 96 | auto handle = ::_popen (command.c_str(), "r"); 97 | #else 98 | auto handle = ::popen (command.c_str(), "r"); 99 | #endif 100 | 101 | if (! handle) 102 | throw std::runtime_error ("choc::execute() failed: " + getErrnoString()); 103 | 104 | std::ostringstream result; 105 | 106 | for (;;) 107 | { 108 | char buffer[1024]; 109 | auto bytesRead = ::fread (buffer, 1, sizeof (buffer), handle); 110 | 111 | try 112 | { 113 | result.write (buffer, (std::streamsize) bytesRead); 114 | 115 | if (bytesRead < sizeof (buffer)) 116 | break; 117 | } 118 | catch (...) { break; } 119 | } 120 | 121 | #ifdef _MSC_VER 122 | auto status = ::_pclose (handle); 123 | #else 124 | auto status = ::pclose (handle); 125 | #endif 126 | return { result.str(), status }; 127 | } 128 | 129 | } // namespace choc 130 | 131 | #endif // CHOC_EXECUTE_HEADER_INCLUDED 132 | -------------------------------------------------------------------------------- /platform/choc_HighResolutionSteadyClock.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_HIGH_RES_CLOCK_HEADER_INCLUDED 20 | #define CHOC_HIGH_RES_CLOCK_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | namespace choc 26 | { 27 | /// If you want a high resolution clock, then std::chrono::high_resolution_clock 28 | /// would seem like the obvious choice.. however, apparently on some systems it 29 | /// can be non-steady, in which case it's best to fall back to steady_clock. 30 | /// This typedef just keeps all that tedious knowledge in one place. 31 | using HighResolutionSteadyClock = std::conditional::type; 34 | } 35 | 36 | #endif // CHOC_HIGH_RES_CLOCK_HEADER_INCLUDED 37 | -------------------------------------------------------------------------------- /platform/choc_ObjectiveCHelpers.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_OBJC_HELPERS_HEADER_INCLUDED 20 | #define CHOC_OBJC_HELPERS_HEADER_INCLUDED 21 | 22 | #include "choc_Platform.h" 23 | 24 | #if CHOC_APPLE 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | #include "choc_Assert.h" 32 | 33 | 34 | //============================================================================== 35 | /** 36 | This namespace contains a random assortment of helpers for reducing the 37 | boilerplate needed when writing C++ code that uses the obj-C API. 38 | 39 | This is an annoying and long-winded way to interop with obj-C, but in a 40 | header-only class that will be included in a C++ compile unit, there's not 41 | really any other way to do it. 42 | */ 43 | namespace choc::objc 44 | { 45 | //============================================================================== 46 | /// Invokes an obj-C selector for a given target and some arbitrary 47 | /// parameters, also allowing you to specify the return type. 48 | template 49 | ReturnType call (id target, const char* selector, Args... args) 50 | { 51 | constexpr const auto msgSend = ([] 52 | { 53 | #if defined (__x86_64__) 54 | if constexpr (std::is_void_v) 55 | return objc_msgSend; 56 | else if constexpr (sizeof (ReturnType) > 16) 57 | return objc_msgSend_stret; 58 | else 59 | return objc_msgSend; 60 | #elif defined (__arm64__) 61 | return objc_msgSend; 62 | #else 63 | #error "Unknown or unsupported architecture!" 64 | #endif 65 | })(); 66 | 67 | return reinterpret_cast (msgSend) (target, sel_registerName (selector), args...); 68 | } 69 | 70 | /// Calls a selector on a named class rather than an object. 71 | template 72 | ReturnType callClass (const char* targetClassName, const char* selector, Args... args) 73 | { 74 | return call ((id) objc_getClass (targetClassName), selector, std::forward (args)...); 75 | } 76 | 77 | /// Invokes an obj-C selector for the superclass of a given target. 78 | template 79 | ReturnType callSuper (id target, const char* selector, Args... args) 80 | { 81 | constexpr const auto msgSendSuper = ([] 82 | { 83 | #if defined (__x86_64__) 84 | if constexpr (std::is_void_v) 85 | return objc_msgSendSuper; 86 | else if constexpr (sizeof (ReturnType) > 16) 87 | return objc_msgSendSuper_stret; 88 | else 89 | return objc_msgSendSuper; 90 | #elif defined (__arm64__) 91 | return objc_msgSendSuper; 92 | #else 93 | #error "Unknown or unsupported architecture!" 94 | #endif 95 | })(); 96 | 97 | objc_super superInfo = 98 | { 99 | target, 100 | (Class) call (target, "superclass") 101 | }; 102 | 103 | return reinterpret_cast (msgSendSuper) (&superInfo, sel_registerName (selector), args...); 104 | } 105 | 106 | //============================================================================== 107 | // This stuff lets you write autorelease scopes that work either with or without 108 | // ARC support being enabled in the compiler. 109 | #if __has_feature(objc_arc) 110 | #define CHOC_AUTORELEASE_BEGIN @autoreleasepool { 111 | #define CHOC_AUTORELEASE_END } 112 | #define CHOC_OBJC_CAST_BRIDGED __bridge 113 | #else 114 | struct AutoReleasePool 115 | { 116 | AutoReleasePool() { pool = callClass ("NSAutoreleasePool", "new"); } 117 | ~AutoReleasePool() { call (pool, "release"); } 118 | 119 | id pool; 120 | }; 121 | 122 | #define CHOC_MAKE_AR_NAME2(line) autoreleasePool_ ## line 123 | #define CHOC_MAKE_AR_NAME1(line) CHOC_MAKE_AR_NAME2(line) 124 | #define CHOC_AUTORELEASE_BEGIN { choc::objc::AutoReleasePool CHOC_MAKE_AR_NAME1(__LINE__); 125 | #define CHOC_AUTORELEASE_END } 126 | #define CHOC_OBJC_CAST_BRIDGED 127 | #endif 128 | 129 | //============================================================================== 130 | /// Creates a delegate class from a base. A random suffix will be added to the 131 | /// new class name provided, to avoid clashes in the global namespace. 132 | inline Class createDelegateClass (const char* baseClass, const char* newClassName) 133 | { 134 | auto time = std::chrono::high_resolution_clock::now().time_since_epoch(); 135 | auto micros = std::chrono::duration_cast (time).count(); 136 | auto uniqueDelegateName = newClassName + std::to_string (static_cast (micros)); 137 | auto c = objc_allocateClassPair (objc_getClass (baseClass), uniqueDelegateName.c_str(), 0); 138 | CHOC_ASSERT (c); 139 | return c; 140 | } 141 | 142 | //============================================================================== 143 | /// Converts an NSString to a std::string 144 | inline std::string getString (id nsString) { if (nsString) return std::string (call (nsString, "UTF8String")); return {}; } 145 | /// Converts a raw UTF8 string to an NSString 146 | inline id getNSString (const char* s) { return callClass ("NSString", "stringWithUTF8String:", s != nullptr ? s : ""); } 147 | /// Converts a UTF8 std::string to an NSString 148 | inline id getNSString (const std::string& s) { return getNSString (s.c_str()); } 149 | 150 | /// Converts a bool to a NSNumber 151 | inline id getNSNumberBool (bool b) { return callClass ("NSNumber", "numberWithBool:", (BOOL) b); } 152 | 153 | /// Invokes `[NSApplication sharedApplication]` 154 | inline id getSharedNSApplication() { return callClass ("NSApplication", "sharedApplication"); } 155 | 156 | //============================================================================== 157 | // Including CodeGraphics.h can create all kinds of messy C/C++ symbol clashes 158 | // with other headers, and in many cases, all we need are these structs.. 159 | #if defined (__LP64__) && __LP64__ 160 | using CGFloat = double; 161 | #else 162 | using CGFloat = float; 163 | #endif 164 | 165 | struct CGPoint { CGFloat x = 0, y = 0; }; 166 | struct CGSize { CGFloat width = 0, height = 0; }; 167 | struct CGRect { CGPoint origin; CGSize size; }; 168 | 169 | } // namespace choc::objc 170 | 171 | #endif // CHOC_APPLE 172 | #endif // CHOC_OBJC_HELPERS_HEADER_INCLUDED 173 | -------------------------------------------------------------------------------- /platform/choc_Platform.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_PLATFORM_DETECT_HEADER_INCLUDED 20 | #define CHOC_PLATFORM_DETECT_HEADER_INCLUDED 21 | 22 | /** 23 | This can be used to check the version of the choc library. 24 | 25 | This should match the value in the VERSION.txt file. 26 | 27 | When you're working on large projects that may contain more than one copy 28 | of the choc library, it's a good idea to use this to make sure that all of 29 | those copies are the same version of the code, otherwise you can run into 30 | some very nasty bugs. 31 | */ 32 | #define CHOC_VERSION 0x10000 33 | 34 | 35 | /* 36 | These conditionals declare the macros 37 | - CHOC_WINDOWS 38 | - CHOC_ANDROID 39 | - CHOC_LINUX 40 | - CHOC_OSX 41 | - CHOC_IOS 42 | - CHOC_BSD 43 | - CHOC_POSIX 44 | - CHOC_EMSCRIPTEN 45 | ...based on the current operating system. 46 | 47 | It also declares a string literal macro CHOC_OPERATING_SYSTEM_NAME 48 | which can be used if you need a text description of the OS. 49 | */ 50 | #if defined (_WIN32) || defined (_WIN64) 51 | #define CHOC_WINDOWS 1 52 | #define CHOC_OPERATING_SYSTEM_NAME "Windows" 53 | #elif __ANDROID__ 54 | #define CHOC_ANDROID 1 55 | #define CHOC_OPERATING_SYSTEM_NAME "Android" 56 | #elif defined (LINUX) || defined (__linux__) 57 | #define CHOC_LINUX 1 58 | #define CHOC_OPERATING_SYSTEM_NAME "Linux" 59 | #elif __APPLE__ 60 | #define CHOC_APPLE 1 61 | #include 62 | #if TARGET_OS_IPHONE || TARGET_IPHONE_SIMULATOR 63 | #define CHOC_IOS 1 64 | #define CHOC_OPERATING_SYSTEM_NAME "iOS" 65 | #else 66 | #define CHOC_OSX 1 67 | #define CHOC_OPERATING_SYSTEM_NAME "OSX" 68 | #endif 69 | #elif defined (__FreeBSD__) || (__OpenBSD__) 70 | #define CHOC_BSD 1 71 | #define CHOC_OPERATING_SYSTEM_NAME "BSD" 72 | #elif defined (_POSIX_VERSION) 73 | #define CHOC_POSIX 1 74 | #define CHOC_OPERATING_SYSTEM_NAME "Posix" 75 | #elif defined (__EMSCRIPTEN__) 76 | #define CHOC_EMSCRIPTEN 1 77 | #define CHOC_OPERATING_SYSTEM_NAME "Emscripten" 78 | #else 79 | #error "Unknown platform!" 80 | #endif 81 | 82 | 83 | #endif // CHOC_PLATFORM_DETECT_HEADER_INCLUDED 84 | -------------------------------------------------------------------------------- /platform/choc_ReenableAllWarnings.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | 20 | // This file re-enables the warnings that were turned off by choc_DisableAllWarnings.h 21 | // So when you have to include some crappy 3rd-party code which is full of warnings, you 22 | // can sandwich it between includes of choc_DisableAllWarnings.h and choc_ReenableAllWarnings.h 23 | 24 | #if __clang__ 25 | #pragma clang diagnostic pop 26 | #elif __GNUC__ 27 | #pragma GCC diagnostic pop 28 | #else 29 | #pragma warning (pop) 30 | #endif 31 | -------------------------------------------------------------------------------- /tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project(choc_tests) 4 | 5 | add_executable(choc_tests main.cpp main2.cpp) 6 | 7 | # To test V8, there needs to be a suitable folder somewhere with a build 8 | # set(V8_LOCATION "/Users/jules/code/v8") 9 | # set(V8_BUILD_NAME "arm64.release") 10 | 11 | # To test the HTTP server, you'll need boost beast and its dependencies, in a 12 | # folder at this location: 13 | # set(BOOST_LOCATION "/Users/jules/code/cmajor-dev/cmajor/3rdParty/boost") 14 | 15 | target_compile_features(choc_tests PRIVATE cxx_std_20) 16 | 17 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang" OR 18 | CMAKE_CXX_COMPILER_ID MATCHES "GNU") 19 | 20 | set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -O3") 21 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -DDEBUG") 22 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror -Wall -Wextra -Wshadow -Wno-missing-field-initializers -Wstrict-aliasing -Wuninitialized -Wunused-parameter -Wconversion -Wsign-compare -Wreorder -Wsign-conversion -Wno-ignored-qualifiers -Wunreachable-code -Wdeprecated-copy -Wdeprecated-copy-dtor -Wpedantic ") 23 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-unused-result") 24 | 25 | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 26 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++ -latomic") 27 | endif() 28 | 29 | # Enable additional Clang warnings 30 | if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") 31 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wint-conversion -Woverloaded-virtual -Wshorten-64-to-32 -Wconditional-uninitialized -Wconstant-conversion -Wunused-private-field -Wbool-conversion -Wextra-semi -Wshadow-uncaptured-local") 32 | 33 | # V8 screws up the sanitizer... 34 | if (DEFINED V8_LOCATION) 35 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DCHOC_V8_AVAILABLE=1 -DV8_COMPRESS_POINTERS=1 -DV8_ENABLE_SANDBOX=1") 36 | else() 37 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined") 38 | endif() 39 | endif() 40 | 41 | if (CMAKE_CXX_COMPILER_ID MATCHES "GNU") 42 | # Disable warnings about ABI changes 43 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-psabi -flarge-source-files") 44 | set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -static-libstdc++") 45 | endif() 46 | 47 | if (WARNINGS_AS_ERRORS) 48 | message ("Treating warnings as errors") 49 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") 50 | endif() 51 | 52 | if (PROFILE) 53 | message ("Enabling profile output") 54 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -pg") 55 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pg") 56 | endif() 57 | 58 | target_compile_features(choc_tests PRIVATE cxx_std_17) 59 | 60 | elseif (CMAKE_CXX_COMPILER_ID MATCHES "MSVC") 61 | 62 | set(CompilerFlags 63 | CMAKE_CXX_FLAGS 64 | CMAKE_CXX_FLAGS_DEBUG 65 | CMAKE_CXX_FLAGS_RELEASE 66 | CMAKE_C_FLAGS 67 | CMAKE_C_FLAGS_DEBUG 68 | CMAKE_C_FLAGS_RELEASE 69 | ) 70 | 71 | set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) 72 | 73 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /EHsc /W4 /WX -DWIN32 /bigobj ") 74 | set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 75 | endif() 76 | 77 | # On OSX, need to add the WebKit framework for WebView functionality 78 | if (${CMAKE_SYSTEM_NAME} MATCHES "Darwin") 79 | target_link_libraries(choc_tests PRIVATE "-framework WebKit") 80 | target_link_libraries(choc_tests PRIVATE "-framework CoreServices") 81 | target_link_libraries(choc_tests PRIVATE "-framework CoreAudio") 82 | target_link_libraries(choc_tests PRIVATE "-framework CoreMIDI") 83 | 84 | if (DEFINED V8_LOCATION) 85 | target_include_directories(choc_tests PRIVATE "${V8_LOCATION}/v8/include") 86 | target_link_directories(choc_tests PRIVATE "${V8_LOCATION}/v8/out.gn/${V8_BUILD_NAME}/obj") 87 | target_link_libraries(choc_tests "-lv8_monolith -lv8_libbase -lv8_libplatform") 88 | endif() 89 | endif() 90 | 91 | # This is an example of how to link the packages required for WebView on Linux 92 | if (${CMAKE_SYSTEM_NAME} MATCHES "Linux") 93 | find_package(PkgConfig REQUIRED) 94 | pkg_check_modules (gtk3 REQUIRED gtk+-3.0 IMPORTED_TARGET) 95 | pkg_check_modules (webkit2 REQUIRED webkit2gtk-4.1 IMPORTED_TARGET) 96 | pkg_check_modules (alsa REQUIRED alsa IMPORTED_TARGET) 97 | pkg_check_modules (jack REQUIRED jack IMPORTED_TARGET) 98 | target_link_libraries (choc_tests PUBLIC pthread PkgConfig::gtk3 PkgConfig::webkit2 PkgConfig::alsa PkgConfig::jack) 99 | endif() 100 | 101 | if (DEFINED BOOST_LOCATION) 102 | add_compile_definitions (choc_tests 103 | CHOC_ENABLE_HTTP_SERVER_TEST=1 104 | ) 105 | 106 | target_include_directories(choc_tests 107 | SYSTEM PRIVATE 108 | ${BOOST_LOCATION}/align/include/ 109 | ${BOOST_LOCATION}/asio/include/ 110 | ${BOOST_LOCATION}/assert/include/ 111 | ${BOOST_LOCATION}/beast/include/ 112 | ${BOOST_LOCATION}/bind/include/ 113 | ${BOOST_LOCATION}/config/include/ 114 | ${BOOST_LOCATION}/core/include/ 115 | ${BOOST_LOCATION}/date_time/include/ 116 | ${BOOST_LOCATION}/endian/include/ 117 | ${BOOST_LOCATION}/intrusive/include/ 118 | ${BOOST_LOCATION}/is_placeholder/include/ 119 | ${BOOST_LOCATION}/io/include/ 120 | ${BOOST_LOCATION}/logic/include/ 121 | ${BOOST_LOCATION}/optional/include/ 122 | ${BOOST_LOCATION}/static_assert/include/ 123 | ${BOOST_LOCATION}/smart_ptr/include/ 124 | ${BOOST_LOCATION}/system/include/ 125 | ${BOOST_LOCATION}/move/include/ 126 | ${BOOST_LOCATION}/mp11/include/ 127 | ${BOOST_LOCATION}/mpl/include/ 128 | ${BOOST_LOCATION}/numeric_conversion/include/ 129 | ${BOOST_LOCATION}/preprocessor/include/ 130 | ${BOOST_LOCATION}/predef/include/ 131 | ${BOOST_LOCATION}/regex/include/ 132 | ${BOOST_LOCATION}/throw_exception/include/ 133 | ${BOOST_LOCATION}/type_traits/include/ 134 | ${BOOST_LOCATION}/utility/include/ 135 | ${BOOST_LOCATION}/winapi/include/ 136 | ${BOOST_LOCATION}/static_string/include/ 137 | ) 138 | endif() 139 | 140 | install(TARGETS choc_tests) 141 | 142 | enable_testing() 143 | add_test(NAME choc_tests COMMAND choc_tests) 144 | -------------------------------------------------------------------------------- /tests/choc_UnitTest.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_UNITTEST_HEADER_INCLUDED 20 | #define CHOC_UNITTEST_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | /** 28 | Maybe the world's smallest, simplest unit-test framework :) 29 | 30 | This was written simply as a very lightweight, dependency-free framework to run choc's 31 | own internal set of unit-tests. Feel free to use it for your own testing needs, but 32 | many other more feature-rich testing frameworks are available! 33 | 34 | For examples of how to structure a test and use the macros, have a look at the 35 | choc_tests.h file. 36 | */ 37 | 38 | namespace choc::test 39 | { 40 | 41 | /// Keeps track of the number of passes/fails for a test-run. 42 | struct TestProgress 43 | { 44 | void startCategory (std::string category); 45 | void startTest (std::string_view testName); 46 | void endTest(); 47 | void fail (const char* filename, int lineNumber, std::string_view message); 48 | void check (bool, const char* filename, int lineNumber, std::string_view failureMessage); 49 | void print (std::string_view); 50 | 51 | /// Call this at the end to print out the number of passes and fails 52 | void printReport(); 53 | 54 | /// This is used by the print() function to emit progress messages as the tests run. 55 | /// If you don't supply your own funciton here, the default will write to std::cout 56 | std::function printMessage; 57 | 58 | std::string currentCategory, currentTest; 59 | int numPasses = 0, numFails = 0; 60 | std::vector failedTests; 61 | 62 | private: 63 | bool currentTestFailed = false; 64 | }; 65 | 66 | // Some macros to use to perform tests. If you want to use this to write your own tests, 67 | // have a look at the tests for choc functions, and it should be pretty obvious how the 68 | // macros are supposed to be used. 69 | 70 | #define CHOC_CATEGORY(category) progress.startCategory (#category); 71 | #define CHOC_TEST(name) choc::test::ScopedTest scopedTest_ ## __LINE__ (progress, #name); 72 | #define CHOC_FAIL(message) progress.fail (__FILE__, __LINE__, message); 73 | #define CHOC_EXPECT_TRUE(b) progress.check (b, __FILE__, __LINE__, "Expected " #b); 74 | #define CHOC_EXPECT_FALSE(b) progress.check (! (b), __FILE__, __LINE__, "Expected ! " #b); 75 | #define CHOC_EXPECT_EQ(a, b) { auto x = a; auto y = b; progress.check (x == y, __FILE__, __LINE__, "Expected " #a " (" + choc::test::convertToString (x) + ") == " #b " (" + choc::test::convertToString (y) + ")"); } 76 | #define CHOC_EXPECT_NE(a, b) { auto x = a; auto y = b; progress.check (x != y, __FILE__, __LINE__, "Expected " #a " (" + choc::test::convertToString (x) + ") != " #b); } 77 | #define CHOC_EXPECT_NEAR(a, b, diff) { auto x = a; auto y = b; auto d = diff; progress.check (std::abs (x - y) <= d, __FILE__, __LINE__, #a " (" + choc::test::convertToString (x) + ") and " #b " (" + choc::test::convertToString (y) + ") differ by more than " + choc::test::convertToString (d)); } 78 | #define CHOC_CATCH_UNEXPECTED_EXCEPTION catch (...) { CHOC_FAIL ("Unexpected exception thrown"); } 79 | 80 | 81 | 82 | //============================================================================== 83 | // _ _ _ _ 84 | // __| | ___ | |_ __ _ (_)| | ___ 85 | // / _` | / _ \| __| / _` || || |/ __| 86 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 87 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 88 | // 89 | // Code beyond this point is implementation detail... 90 | // 91 | //============================================================================== 92 | 93 | inline void TestProgress::print (std::string_view message) 94 | { 95 | if (printMessage != nullptr) 96 | printMessage (message); 97 | else 98 | std::cout << message << std::endl; 99 | } 100 | 101 | inline void TestProgress::startCategory (std::string category) 102 | { 103 | currentCategory = std::move (category); 104 | } 105 | 106 | inline void TestProgress::startTest (std::string_view testName) 107 | { 108 | CHOC_ASSERT (! currentCategory.empty()); 109 | currentTest = currentCategory + "/" + std::string (testName); 110 | currentTestFailed = false; 111 | print ("[ RUN ] " + currentTest); 112 | } 113 | 114 | inline void TestProgress::endTest() 115 | { 116 | if (currentTestFailed) 117 | { 118 | print ("[ FAIL ] " + currentTest); 119 | ++numFails; 120 | failedTests.push_back (currentTest); 121 | } 122 | else 123 | { 124 | print ("[ OK ] " + currentTest); 125 | ++numPasses; 126 | } 127 | 128 | currentTest = {}; 129 | } 130 | 131 | inline void TestProgress::fail (const char* filename, int lineNumber, std::string_view message) 132 | { 133 | currentTestFailed = true; 134 | CHOC_ASSERT (! currentTest.empty()); 135 | print ("FAILED: " + std::string (filename) + ":" + std::to_string (lineNumber)); 136 | print (message); 137 | } 138 | 139 | inline void TestProgress::check (bool condition, const char* filename, int lineNumber, std::string_view message) 140 | { 141 | if (! condition) 142 | fail (filename, lineNumber, message); 143 | } 144 | 145 | inline void TestProgress::printReport() 146 | { 147 | print ("========================================================"); 148 | print (" Passed: " + std::to_string (numPasses)); 149 | print (" Failed: " + std::to_string (numFails)); 150 | 151 | for (auto& failed : failedTests) 152 | print (" Failed test: " + failed); 153 | 154 | print ("========================================================"); 155 | } 156 | 157 | struct ScopedTest 158 | { 159 | ScopedTest (TestProgress& p, std::string name) : progress (p) { progress.startTest (std::move (name)); } 160 | ~ScopedTest() { progress.endTest(); } 161 | 162 | TestProgress& progress; 163 | }; 164 | 165 | template 166 | std::string convertToString (Type n) 167 | { 168 | if constexpr (std::is_same::value) return std::string (n); 169 | else if constexpr (std::is_same::value) return n; 170 | else if constexpr (std::is_same::value) return std::string (n); 171 | else return std::to_string (n); 172 | } 173 | 174 | } // namespace choc::test 175 | 176 | #endif // CHOC_UNITTEST_HEADER_INCLUDED 177 | -------------------------------------------------------------------------------- /tests/choc_webserver_example.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #if __has_include() && __has_include() 20 | 21 | #include "../network/choc_HTTPServer.h" 22 | 23 | static constexpr std::string_view demoPageHTML = R"xx( 24 | 25 | 26 | CHOC webserver demo 27 | 28 | 29 |

CHOC webserver demo

30 | 31 |

This page is being served by an instance of a choc::network::HTTPServer

32 |

It connects a websocket to the C++ back-end and shows how to send messages either way...

33 |

34 |

35 | 36 | 37 | 62 | 63 | 64 | )xx"; 65 | 66 | choc::network::HTTPServer server; 67 | 68 | //============================================================================== 69 | // This is the object that we'll use to communicate with each instance of a connected client. 70 | struct ExampleClientInstance : public choc::network::HTTPServer::ClientInstance 71 | { 72 | ExampleClientInstance() 73 | { 74 | static int clientCount = 0; 75 | clientID = ++clientCount; 76 | 77 | std::cout << "New client connected, ID: " << clientID << std::endl; 78 | } 79 | 80 | ~ExampleClientInstance() 81 | { 82 | std::cout << "Client ID " << clientID << " disconnected" << std::endl; 83 | } 84 | 85 | choc::network::HTTPContent getHTTPContent (std::string_view path) override 86 | { 87 | // This path is asking for the default page content 88 | if (path == "/") 89 | { 90 | // When we return the content for the page, we'll embed the URL into it that 91 | // it'll need to use to open the websocket back to this address.. 92 | auto pageContent = choc::text::replace (demoPageHTML, 93 | "SOCKET_URL", choc::json::getEscapedQuotedString (server.getWebSocketAddress())); 94 | 95 | return choc::network::HTTPContent::forHTML (pageContent); 96 | } 97 | 98 | // If you want to serve content for other paths, you would do that here... 99 | 100 | return {}; 101 | } 102 | 103 | void upgradedToWebSocket (std::string_view path) override 104 | { 105 | std::cout << "Client ID " << clientID << " opened websocket for path: " << path << std::endl; 106 | } 107 | 108 | void handleWebSocketMessage (std::string_view message) override 109 | { 110 | std::cout << "Client ID " << clientID << " received websocket message: " << message << std::endl; 111 | 112 | // for this demo, we'll just bounce back the same message we received, but 113 | // obviously this can be anything.. 114 | sendWebSocketMessage (std::string (message)); 115 | } 116 | 117 | int clientID = 0; 118 | }; 119 | 120 | //============================================================================== 121 | inline int runDemoWebServer() 122 | { 123 | auto address = "127.0.0.1"; 124 | uint16_t preferredPortNum = 3000; 125 | 126 | bool openedOK = server.open (address, preferredPortNum, 0, 127 | [] 128 | { 129 | // Create a new object for each client.. 130 | return std::make_unique(); 131 | }, 132 | [] (const std::string& error) 133 | { 134 | // Handle some kind of server error.. 135 | std::cout << "Error from webserver: " << error << std::endl; 136 | }); 137 | 138 | if (! openedOK) 139 | return 1; 140 | 141 | std::cout << "HTTP server is running!" << std::endl 142 | << std::endl 143 | << "Use a browser to view it at: " << server.getHTTPAddress() << std::endl; 144 | 145 | // While the server is running, this thread no longer needs to be involved. 146 | // For this command-line demo we'll just sleep, but you could also run the 147 | // message loop or get on with other tasks. 148 | 149 | for (;;) 150 | std::this_thread::sleep_for (std::chrono::seconds (1)); 151 | 152 | return 0; 153 | } 154 | 155 | #else 156 | 157 | inline int runDemoWebServer() 158 | { 159 | std::cout << "If you want to build the web-server demo, you'll need to have boost::beast, boost::asio and their dependencies on your include path." << std::endl 160 | << "To use them in this test app, see the BOOST_LOCATION variable in CMakeLists.txt" << std::endl; 161 | return 1; 162 | } 163 | 164 | #endif 165 | -------------------------------------------------------------------------------- /tests/choc_webview_example.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #include "../gui/choc_DesktopWindow.h" 20 | 21 | inline int openDemoWebViewWindow() 22 | { 23 | choc::messageloop::initialise(); // Tells the message loop that this is the message thread 24 | choc::ui::setWindowsDPIAwareness(); // For Windows, we need to tell the OS we're high-DPI-aware 25 | 26 | choc::ui::DesktopWindow window ({ 100, 100, 800, 600 }); 27 | 28 | window.setWindowTitle ("Hello"); 29 | window.setResizable (true); 30 | window.setMinimumSize (300, 300); 31 | window.setMaximumSize (1500, 1200); 32 | window.windowClosed = [] { choc::messageloop::stop(); }; 33 | 34 | choc::ui::WebView::Options options; 35 | options.enableDebugMode = true; 36 | 37 | options.webviewIsReady = [&] (choc::ui::WebView& webview) 38 | { 39 | CHOC_ASSERT (webview.loadedOK()); 40 | 41 | window.setContent (webview.getViewHandle()); 42 | 43 | CHOC_ASSERT (webview.bind ("eventCallbackFn", [] (const choc::value::ValueView& args) -> choc::value::Value 44 | { 45 | auto message = "eventCallbackFn() called with args: " + choc::json::toString (args); 46 | 47 | // This just shows how to invoke an async callback 48 | choc::messageloop::postMessage ([message] 49 | { 50 | std::cout << "WebView callback message: " << message << std::endl; 51 | }); 52 | 53 | return choc::value::createString (message); 54 | })); 55 | 56 | CHOC_ASSERT (webview.bind ("loadCHOCWebsite", [&webview] (const choc::value::ValueView&) -> choc::value::Value 57 | { 58 | webview.navigate ("https://github.com/Tracktion/choc"); 59 | return {}; 60 | })); 61 | 62 | CHOC_ASSERT (webview.setHTML (R"xxx( 63 | 64 | Page Title 65 | 76 | 77 | 78 |

CHOC WebView Demo

79 |

This is a demo of a choc::ui::WebView window

80 |

81 |

82 |

83 |

84 | 85 | 86 | )xxx")); 87 | 88 | window.toFront(); 89 | }; 90 | 91 | choc::ui::WebView webview (options); 92 | 93 | choc::messageloop::run(); 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /tests/main.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #include "choc_tests.h" 20 | #include "../containers/choc_ArgumentList.h" 21 | #include "choc_webview_example.h" 22 | #include "choc_webserver_example.h" 23 | 24 | //============================================================================== 25 | int main (int argc, const char** argv) 26 | { 27 | choc::ArgumentList args (argc, argv); 28 | 29 | if (args.contains ("webview")) 30 | return openDemoWebViewWindow(); 31 | 32 | if (args.contains ("webserver")) 33 | return runDemoWebServer(); 34 | 35 | choc::test::TestProgress progress; 36 | return choc_unit_tests::runAllTests (progress, args.contains ("--multithread")) ? 0 : 1; 37 | } 38 | 39 | //============================================================================== 40 | // include this last to make sure the tests don't rely on any of the garbage 41 | // that gets dragged in.. 42 | #include "../javascript/choc_javascript_Duktape.h" 43 | #include "../javascript/choc_javascript_QuickJS.h" 44 | 45 | #if CHOC_V8_AVAILABLE 46 | #include "../javascript/choc_javascript_V8.h" 47 | #endif 48 | -------------------------------------------------------------------------------- /tests/main2.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | 20 | 21 | // This empty-ish file exists so that we test the inclusion of all the 22 | // headers in more than one compile unit, That should catch any 23 | // non-inlined symbols that will cause duplicate function errors 24 | // at link time. 25 | 26 | 27 | // On Windows, we'll include windows.h before the headers, in contrast to what 28 | // happens in main.cpp 29 | #if defined (_WIN32) || defined (_WIN64) 30 | #include 31 | #endif 32 | 33 | #ifndef __MINGW32__ 34 | // This can only be included in one translation unit 35 | #include "../audio/io/choc_RtAudioPlayer.h" 36 | #include "../audio/io/choc_RenderingAudioMIDIPlayer.h" 37 | #endif 38 | 39 | // This one pulls in windows.h so keep it out of choc_tests.h 40 | #include "../platform/choc_MemoryDLL.h" 41 | 42 | #include "choc_tests.h" 43 | -------------------------------------------------------------------------------- /tests/test.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tracktion/choc/1df9d42836922a651acd08651c72341c72da7ffa/tests/test.zip -------------------------------------------------------------------------------- /text/choc_OpenSourceLicenseList.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_LICENSETEXTCOLLECTOR_HEADER_INCLUDED 20 | #define CHOC_LICENSETEXTCOLLECTOR_HEADER_INCLUDED 21 | 22 | #include "choc_StringUtilities.h" 23 | 24 | namespace choc::text 25 | { 26 | 27 | //============================================================================== 28 | /* 29 | If you're using CHOC and/or other open-source libraries in your product, 30 | then you're legally obliged to comply with their license conditions, and 31 | these usually require you to reproduce their copyright notices somewhere 32 | in your app. 33 | 34 | To make this boring task easier, this class takes care of collecting together 35 | all the text from many licenses in one place, and also makes sure that 36 | only the licenses from code that is used by your app will be added to 37 | the list (so for example, choc contains some Windows-only licenses which 38 | won't appear in the list when building for other platforms). 39 | 40 | To use it to get the complete set of CHOC licenses, simply make sure you 41 | include this header *before* all the choc headers that you use. All the 42 | choc files that embed 3rd-party code will make sure that the appropriate 43 | license text is added to the global OpenSourceLicenseList object. 44 | 45 | Then, in your app, you can just print the string returned by 46 | `OpenSourceLicenseList::getAllLicenseText()`, and it should contain all 47 | the license text that you need. 48 | 49 | If you have code containing your own license text that you want to 50 | add to the list, simply make sure that your code includes this header, 51 | and then use the CHOC_REGISTER_OPEN_SOURCE_LICENCE macro to declare it. 52 | You can find examples of how to use this macro scattered around the 53 | choc codebase. 54 | */ 55 | struct OpenSourceLicenseList 56 | { 57 | OpenSourceLicenseList(); 58 | 59 | /// Returns a string containing all the licenses. 60 | static std::string getAllLicenseText(); 61 | 62 | /// Adds a license to the list (this will de-dupe any identical 63 | /// licenses that are added more than once). 64 | static void addLicense (std::string text); 65 | 66 | /// Returns the global instance of this class which is used to 67 | /// collect all the licenses into one place. 68 | static OpenSourceLicenseList& getInstance(); 69 | 70 | /// The licenses are simply kept in a vector. 71 | std::vector licenses; 72 | }; 73 | 74 | /// This macro declares a static object whose constructor will automatically 75 | /// add a license to the global list. This means that you can place this macro 76 | /// anywhere in your code and it'll be automatically registered at startup. 77 | /// The libraryName must be a C++ identifier that's used to distinguish 78 | /// different licenses 79 | #define CHOC_REGISTER_OPEN_SOURCE_LICENCE(libraryName, licenseText) \ 80 | struct LicenseAdder_ ## libraryName \ 81 | { \ 82 | LicenseAdder_ ## libraryName() { choc::text::OpenSourceLicenseList::addLicense (licenseText); } \ 83 | }; \ 84 | static inline LicenseAdder_ ## libraryName staticLicenseAdder_ ## libraryName; 85 | 86 | //============================================================================== 87 | // _ _ _ _ 88 | // __| | ___ | |_ __ _ (_)| | ___ 89 | // / _` | / _ \| __| / _` || || |/ __| 90 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 91 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 92 | // 93 | // Code beyond this point is implementation detail... 94 | // 95 | //============================================================================== 96 | 97 | static inline void addLicenseToList (std::vector& list, std::string&& text) 98 | { 99 | list.push_back (choc::text::trim (std::move (text))); 100 | 101 | // sort alphbetically because otherwise the results will differ 102 | // depending on link order 103 | std::sort (list.begin(), list.end()); 104 | list.erase (std::unique (list.begin(), list.end()), list.end()); 105 | } 106 | 107 | inline OpenSourceLicenseList::OpenSourceLicenseList() 108 | { 109 | // First, add our own license.. 110 | addLicenseToList (licenses, R"( 111 | ============================================================================== 112 | CHOC is (C)2022 Julian Storer/Tracktion Corporation, and may be used under 113 | the terms of the ISC license: 114 | 115 | Permission to use, copy, modify, and/or distribute this software for any 116 | purpose with or without fee is hereby granted, provided that the above 117 | copyright notice and this permission notice appear in all copies. 118 | 119 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 120 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 121 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 122 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 123 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 124 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 125 | PERFORMANCE OF THIS SOFTWARE. 126 | )"); 127 | } 128 | 129 | inline OpenSourceLicenseList& OpenSourceLicenseList::getInstance() 130 | { 131 | static OpenSourceLicenseList instance; 132 | return instance; 133 | } 134 | 135 | inline std::string OpenSourceLicenseList::getAllLicenseText() 136 | { 137 | return choc::text::joinStrings (getInstance().licenses, "\n\n") + "\n\n"; 138 | } 139 | 140 | inline void OpenSourceLicenseList::addLicense (std::string text) 141 | { 142 | addLicenseToList (getInstance().licenses, std::move (text)); 143 | } 144 | 145 | } // namespace choc::text 146 | 147 | #endif // CHOC_LICENSETEXTCOLLECTOR_HEADER_INCLUDED 148 | -------------------------------------------------------------------------------- /text/choc_TextTable.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_TEXT_TABLE_HEADER_INCLUDED 20 | #define CHOC_TEXT_TABLE_HEADER_INCLUDED 21 | 22 | #include "choc_StringUtilities.h" 23 | 24 | namespace choc::text 25 | { 26 | 27 | /** 28 | A class to build and format basic text tables with columns that are padded to 29 | make them vertically-aligned. 30 | 31 | Just create a TextTable object, then 32 | - add items to the current row with the << operator 33 | - start a new row with newRow() 34 | - when done, use the toString and getRows() methods to get the result. 35 | 36 | Rows can have a different number of columns, and the table's size is based on the 37 | maximum number of columns. 38 | */ 39 | struct TextTable 40 | { 41 | TextTable() = default; 42 | ~TextTable() = default; 43 | 44 | /// Appends a column to the current row. 45 | TextTable& operator<< (std::string_view text); 46 | 47 | /// Clears and resets the table 48 | void clear(); 49 | 50 | /// Ends the current row, so that subsequent new columns will be added to a new row 51 | void newRow(); 52 | 53 | /// Returns the current number of rows 54 | size_t getNumRows() const; 55 | 56 | /// Finds the number of columns (by looking for the row with the most columns) 57 | size_t getNumColumns() const; 58 | 59 | /// Returns a vector of strings for each each row, which will have been 60 | /// padded and formatted to align vertically. Each row will have the prefix 61 | /// and suffix strings attached, and the columnSeparator string added between 62 | /// each column. 63 | std::vector getRows (std::string_view rowPrefix, 64 | std::string_view columnSeparator, 65 | std::string_view rowSuffix) const; 66 | 67 | /// Returns a string containing all the rows in the table. 68 | /// See getRows() for details about the format strings. This method simply 69 | /// concatenates all the strings for each row. 70 | std::string toString (std::string_view rowPrefix, 71 | std::string_view columnSeparator, 72 | std::string_view rowSuffix) const; 73 | 74 | private: 75 | //============================================================================== 76 | struct Row 77 | { 78 | std::vector columns; 79 | 80 | void addColumn (std::string_view text); 81 | std::string toString (std::string_view columnSeparator, const std::vector widths) const; 82 | }; 83 | 84 | std::vector rows; 85 | bool startNewRow = true; 86 | 87 | std::vector getColumnWidths() const; 88 | }; 89 | 90 | 91 | //============================================================================== 92 | // _ _ _ _ 93 | // __| | ___ | |_ __ _ (_)| | ___ 94 | // / _` | / _ \| __| / _` || || |/ __| 95 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 96 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 97 | // 98 | // Code beyond this point is implementation detail... 99 | // 100 | //============================================================================== 101 | 102 | inline TextTable& TextTable::operator<< (std::string_view text) 103 | { 104 | if (startNewRow) 105 | { 106 | startNewRow = false; 107 | rows.push_back ({}); 108 | } 109 | 110 | rows.back().addColumn (text); 111 | return *this; 112 | } 113 | 114 | inline void TextTable::newRow() 115 | { 116 | if (startNewRow) 117 | rows.push_back ({}); 118 | else 119 | startNewRow = true; 120 | } 121 | 122 | inline void TextTable::clear() 123 | { 124 | rows.clear(); 125 | startNewRow = true; 126 | } 127 | 128 | inline size_t TextTable::getNumRows() const 129 | { 130 | return rows.size(); 131 | } 132 | 133 | inline size_t TextTable::getNumColumns() const 134 | { 135 | size_t maxColummns = 0; 136 | 137 | for (auto& row : rows) 138 | maxColummns = std::max (maxColummns, row.columns.size()); 139 | 140 | return maxColummns; 141 | } 142 | 143 | inline std::vector TextTable::getRows (std::string_view rowPrefix, 144 | std::string_view columnSeparator, 145 | std::string_view rowSuffix) const 146 | { 147 | std::vector result; 148 | result.reserve (rows.size()); 149 | auto widths = getColumnWidths(); 150 | 151 | for (auto& row : rows) 152 | result.push_back (std::string (rowPrefix) 153 | + row.toString (columnSeparator, widths) 154 | + std::string (rowSuffix)); 155 | 156 | return result; 157 | } 158 | 159 | inline std::string TextTable::toString (std::string_view rowPrefix, 160 | std::string_view columnSeparator, 161 | std::string_view rowSuffix) const 162 | { 163 | std::string result; 164 | 165 | for (auto& row : getRows (rowPrefix, columnSeparator, rowSuffix)) 166 | result += row; 167 | 168 | return result; 169 | } 170 | 171 | inline std::vector TextTable::getColumnWidths() const 172 | { 173 | std::vector widths; 174 | widths.resize (getNumColumns()); 175 | 176 | for (auto& row : rows) 177 | for (size_t i = 0; i < row.columns.size(); ++i) 178 | widths[i] = std::max (widths[i], row.columns[i].length()); 179 | 180 | return widths; 181 | } 182 | 183 | inline void TextTable::Row::addColumn (std::string_view text) 184 | { 185 | columns.emplace_back (text); 186 | } 187 | 188 | inline std::string TextTable::Row::toString (std::string_view columnSeparator, const std::vector widths) const 189 | { 190 | std::string result; 191 | size_t index = 0; 192 | 193 | for (auto& width : widths) 194 | { 195 | if (index != 0) 196 | result += columnSeparator; 197 | 198 | std::string padded; 199 | 200 | if (index < columns.size()) 201 | padded = columns[index]; 202 | 203 | padded.resize (width, ' '); 204 | result += padded; 205 | ++index; 206 | } 207 | 208 | return result; 209 | } 210 | 211 | } // namespace choc::text 212 | 213 | #endif 214 | -------------------------------------------------------------------------------- /text/choc_Wildcard.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_WILDCARD_HEADER_INCLUDED 20 | #define CHOC_WILDCARD_HEADER_INCLUDED 21 | 22 | #include "../text/choc_UTF8.h" 23 | 24 | namespace choc::text 25 | { 26 | 27 | //============================================================================== 28 | /** 29 | A wildcard pattern-matcher for filename-style patterns. 30 | 31 | This handles the kind of simple wildcards that you'd give to a filesystem search. 32 | It recognises: 33 | - '*' = any sequence of zero or more characters 34 | - '?' = any character 35 | It accepts a string containing multiple patterns separated by semi-colons, and 36 | considers it a successful match if any of these match. 37 | 38 | E.g. "*.foo;*.blah" will match strings that end either either ".foo" or ".blah". 39 | */ 40 | struct WildcardPattern 41 | { 42 | WildcardPattern() = default; 43 | WildcardPattern (WildcardPattern&&) = default; 44 | WildcardPattern& operator= (WildcardPattern&&) = default; 45 | 46 | /// Creates a matcher for the given pattern. 47 | WildcardPattern (std::string_view pattern, bool caseSensitive); 48 | 49 | /// Returns true if the given string matches the pattern. 50 | bool matches (const std::string& text) const; 51 | 52 | /// You can iterate the pattern strings within the wildcard 53 | std::vector::iterator begin() { return patterns.begin(); } 54 | std::vector::iterator end() { return patterns.end(); } 55 | 56 | private: 57 | std::vector patterns; 58 | bool isCaseSensitive; 59 | 60 | bool matchesAnywhere (choc::text::UTF8Pointer, choc::text::UTF8Pointer) const; 61 | bool matchesAll (choc::text::UTF8Pointer, choc::text::UTF8Pointer) const; 62 | }; 63 | 64 | 65 | 66 | //============================================================================== 67 | // _ _ _ _ 68 | // __| | ___ | |_ __ _ (_)| | ___ 69 | // / _` | / _ \| __| / _` || || |/ __| 70 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 71 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 72 | // 73 | // Code beyond this point is implementation detail... 74 | // 75 | //============================================================================== 76 | 77 | inline WildcardPattern::WildcardPattern (std::string_view pattern, bool caseSensitive) 78 | : isCaseSensitive (caseSensitive) 79 | { 80 | for (auto& p : choc::text::splitString (pattern, ';', false)) 81 | patterns.push_back (choc::text::trim (p)); 82 | } 83 | 84 | inline bool WildcardPattern::matches (const std::string& text) const 85 | { 86 | if (patterns.empty()) 87 | return true; 88 | 89 | choc::text::UTF8Pointer t (text.c_str()); 90 | 91 | for (auto& p : patterns) 92 | if (matchesAll (t, choc::text::UTF8Pointer (p.c_str()))) 93 | return true; 94 | 95 | return false; 96 | } 97 | 98 | inline bool WildcardPattern::matchesAnywhere (choc::text::UTF8Pointer source, choc::text::UTF8Pointer pattern) const 99 | { 100 | while (! source.empty()) 101 | { 102 | if (matchesAll (source, pattern)) 103 | return true; 104 | 105 | ++source; 106 | } 107 | 108 | return false; 109 | } 110 | 111 | inline bool WildcardPattern::matchesAll (choc::text::UTF8Pointer source, choc::text::UTF8Pointer pattern) const 112 | { 113 | for (;;) 114 | { 115 | auto patternChar = pattern.popFirstChar(); 116 | 117 | if (patternChar == '*') 118 | return pattern.empty() || matchesAnywhere (source, pattern); 119 | 120 | auto sourceChar = source.popFirstChar(); 121 | 122 | if (! (sourceChar == patternChar 123 | || (! isCaseSensitive && std::towupper (static_cast (sourceChar)) == std::towupper (static_cast (patternChar))) 124 | || (patternChar == '?' && sourceChar != 0))) 125 | return false; 126 | 127 | if (patternChar == 0) 128 | return true; 129 | } 130 | } 131 | 132 | } // namespace choc::text 133 | 134 | #endif // CHOC_WILDCARD_HEADER_INCLUDED 135 | -------------------------------------------------------------------------------- /threading/choc_SpinLock.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_SPINLOCK_HEADER_INCLUDED 20 | #define CHOC_SPINLOCK_HEADER_INCLUDED 21 | 22 | #include 23 | 24 | namespace choc::threading 25 | { 26 | 27 | //============================================================================== 28 | /** 29 | A minimal no-frills spin-lock. 30 | 31 | To use an RAII pattern for locking a SpinLock, it's compatible with the normal 32 | scoped guard locking classes in the standard library. 33 | */ 34 | struct SpinLock 35 | { 36 | SpinLock() = default; 37 | ~SpinLock() = default; 38 | 39 | void lock(); 40 | bool try_lock(); 41 | void unlock(); 42 | 43 | private: 44 | std::atomic_flag flag = ATOMIC_FLAG_INIT; 45 | }; 46 | 47 | 48 | //============================================================================== 49 | // _ _ _ _ 50 | // __| | ___ | |_ __ _ (_)| | ___ 51 | // / _` | / _ \| __| / _` || || |/ __| 52 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 53 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 54 | // 55 | // Code beyond this point is implementation detail... 56 | // 57 | //============================================================================== 58 | 59 | inline void SpinLock::lock() { while (flag.test_and_set (std::memory_order_acquire)) {} } 60 | inline bool SpinLock::try_lock() { return ! flag.test_and_set (std::memory_order_acquire); } 61 | inline void SpinLock::unlock() { flag.clear(); } 62 | 63 | } // choc::fifo 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /threading/choc_TaskThread.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_TASKTHREAD_HEADER_INCLUDED 20 | #define CHOC_TASKTHREAD_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include "../platform/choc_Assert.h" 27 | 28 | namespace choc::threading 29 | { 30 | 31 | //============================================================================== 32 | /** 33 | Manages a thread which will invoke a given callback function, either 34 | repeatedly at a given time-interval, or in response to another 35 | thread calling TaskThread::trigger(). 36 | 37 | It's quite common to need a thread which performs a background task repeatedly, 38 | with a period of sleeping in between. But the standard library makes it quite a 39 | palaver to do this in a way that lets you interrupt the sleep to either retrigger 40 | the task immediately, or to destroy the thread. This class makes that job 41 | nice and easy. 42 | */ 43 | struct TaskThread 44 | { 45 | TaskThread(); 46 | ~TaskThread(); 47 | 48 | /// Starts the thread running with a given interval and task function. 49 | /// If repeatIntervalMillisecs == 0, the task function will be invoked 50 | /// only when trigger() is called. If the interval is > 0, then 51 | /// whenever this number of milliseconds have passed without it being 52 | /// triggered, it'll be automatically invoked again. 53 | /// If the thread is already running when this it called, it will 54 | /// first be stopped. 55 | template 56 | void start (std::chrono::duration repeatInterval, 57 | std::function task); 58 | 59 | /// Starts the thread running with a given interval and task function. 60 | /// If repeatInterval has no length, the task function will be invoked 61 | /// only when trigger() is called. If the interval is > 0, then 62 | /// whenever this time has passed without it being triggered, it'll be 63 | /// automatically invoked again. 64 | /// If the thread is already running when this it called, it will 65 | /// first be stopped. 66 | void start (uint32_t repeatIntervalMillisecs, 67 | std::function task); 68 | 69 | /// Stops the thread, waiting for it to finish. This may involve 70 | /// waiting for the callback function to complete if it's currently 71 | /// in progress. 72 | void stop(); 73 | 74 | /// This causes the task to be invoked as soon as the thread 75 | /// is free to do so. Calling it multiple times may result in 76 | /// only one call to the task being invoked. 77 | void trigger(); 78 | 79 | private: 80 | //============================================================================== 81 | std::function taskFunction; 82 | std::thread thread; 83 | std::atomic_flag flag = ATOMIC_FLAG_INIT; 84 | std::atomic threadShouldExit { false }; 85 | uint32_t interval = 0; 86 | 87 | void wait(); 88 | }; 89 | 90 | 91 | //============================================================================== 92 | // _ _ _ _ 93 | // __| | ___ | |_ __ _ (_)| | ___ 94 | // / _` | / _ \| __| / _` || || |/ __| 95 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 96 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 97 | // 98 | // Code beyond this point is implementation detail... 99 | // 100 | //============================================================================== 101 | 102 | inline TaskThread::TaskThread() = default; 103 | inline TaskThread::~TaskThread() { stop(); } 104 | 105 | inline void TaskThread::start (uint32_t repeatIntervalMillisecs, std::function f) 106 | { 107 | CHOC_ASSERT (f != nullptr); 108 | 109 | stop(); 110 | taskFunction = std::move (f); 111 | interval = repeatIntervalMillisecs; 112 | threadShouldExit = false; 113 | flag.test_and_set (std::memory_order_acquire); 114 | 115 | thread = std::thread ([this] 116 | { 117 | wait(); 118 | 119 | while (! threadShouldExit) 120 | { 121 | taskFunction(); 122 | wait(); 123 | } 124 | }); 125 | } 126 | 127 | template 128 | void TaskThread::start (std::chrono::duration i, std::function task) 129 | { 130 | start (static_cast (std::chrono::duration_cast (i).count()), 131 | std::move (task)); 132 | } 133 | 134 | inline void TaskThread::stop() 135 | { 136 | if (thread.joinable()) 137 | { 138 | threadShouldExit = true; 139 | trigger(); 140 | thread.join(); 141 | } 142 | } 143 | 144 | inline void TaskThread::trigger() 145 | { 146 | flag.clear (std::memory_order_release); 147 | } 148 | 149 | inline void TaskThread::wait() 150 | { 151 | if (! flag.test_and_set (std::memory_order_acquire)) 152 | return; 153 | 154 | uint32_t numTries = 0; 155 | 156 | auto yieldOrSleep = [&numTries] 157 | { 158 | static constexpr uint32_t numTriesBeforeSleeping = 1000; 159 | static constexpr uint32_t sleepDuration = 5; 160 | 161 | if (numTries == numTriesBeforeSleeping) 162 | { 163 | std::this_thread::sleep_for (std::chrono::milliseconds (sleepDuration)); 164 | } 165 | else 166 | { 167 | std::this_thread::yield(); 168 | ++numTries; 169 | } 170 | }; 171 | 172 | if (interval != 0) 173 | { 174 | auto timeout = std::chrono::high_resolution_clock::now() 175 | + std::chrono::milliseconds (interval); 176 | 177 | for (;;) 178 | { 179 | yieldOrSleep(); 180 | 181 | if (! flag.test_and_set (std::memory_order_acquire)) 182 | return; 183 | 184 | if (std::chrono::high_resolution_clock::now() >= timeout) 185 | return; 186 | } 187 | } 188 | else 189 | { 190 | for (;;) 191 | { 192 | yieldOrSleep(); 193 | 194 | if (! flag.test_and_set (std::memory_order_acquire)) 195 | return; 196 | } 197 | } 198 | } 199 | 200 | 201 | } // namespace choc::threading 202 | 203 | #endif // CHOC_TASKTHREAD_HEADER_INCLUDED 204 | -------------------------------------------------------------------------------- /threading/choc_ThreadSafeFunctor.h: -------------------------------------------------------------------------------- 1 | // 2 | // ██████ ██  ██  ██████  ██████ 3 | // ██      ██  ██ ██    ██ ██       ** Classy Header-Only Classes ** 4 | // ██  ███████ ██  ██ ██ 5 | // ██  ██   ██ ██  ██ ██ https://github.com/Tracktion/choc 6 | //  ██████ ██  ██  ██████   ██████ 7 | // 8 | // CHOC is (C)2022 Tracktion Corporation, and is offered under the terms of the ISC license: 9 | // 10 | // Permission to use, copy, modify, and/or distribute this software for any purpose with or 11 | // without fee is hereby granted, provided that the above copyright notice and this permission 12 | // notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 13 | // WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 14 | // AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR 15 | // CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, 16 | // WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 17 | // CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 | 19 | #ifndef CHOC_THREADSAFEFUNCTOR_HEADER_INCLUDED 20 | #define CHOC_THREADSAFEFUNCTOR_HEADER_INCLUDED 21 | 22 | #include 23 | #include 24 | 25 | namespace choc::threading 26 | { 27 | 28 | //============================================================================== 29 | /** 30 | This acts as a smart-pointer to a shared function, which can be invoked and 31 | cleared in a thread-safe way. So all calls to invoke or to change the target 32 | function are safely locked. 33 | 34 | A good use-case for this is if you trigger an async callback using 35 | something like `choc::messageloop::postMessage` but need the option to be 36 | able to cancel the callback before it arrives. 37 | */ 38 | template 39 | struct ThreadSafeFunctor 40 | { 41 | ThreadSafeFunctor(); 42 | ThreadSafeFunctor (FunctionType&&); 43 | 44 | ThreadSafeFunctor (const ThreadSafeFunctor&) = default; 45 | ThreadSafeFunctor (ThreadSafeFunctor&&) = default; 46 | ThreadSafeFunctor& operator= (const ThreadSafeFunctor&) = default; 47 | ThreadSafeFunctor& operator= (ThreadSafeFunctor&&) = default; 48 | ~ThreadSafeFunctor() = default; 49 | 50 | /// Changes the function that will be called - this is thread-safe 51 | /// and will block until it's safe to change the function 52 | ThreadSafeFunctor& operator= (FunctionType&& newFunctor); 53 | 54 | /// Changes the function that will be called - this is thread-safe 55 | /// and will block until it's safe to change the function 56 | ThreadSafeFunctor& operator= (const FunctionType& newFunctor); 57 | 58 | /// Tries to invoke the function if one has been set. Returns true if 59 | /// the target function is valid and was invoked, or false if there was 60 | /// no function to call. 61 | template 62 | bool operator() (Args&&... args) const; 63 | 64 | /// Returns true if the function isn't null (although it's still safe to 65 | /// call this when it's null, and it will do nothing) 66 | operator bool() const; 67 | 68 | /// Clears the function - this is thread-safe, and will block until 69 | /// the function can safely be changed 70 | void reset(); 71 | 72 | 73 | private: 74 | //============================================================================== 75 | struct CallbackHolder : public std::enable_shared_from_this 76 | { 77 | std::recursive_mutex lock; 78 | FunctionType fn; 79 | }; 80 | 81 | std::shared_ptr callback; 82 | }; 83 | 84 | 85 | //============================================================================== 86 | // _ _ _ _ 87 | // __| | ___ | |_ __ _ (_)| | ___ 88 | // / _` | / _ \| __| / _` || || |/ __| 89 | // | (_| || __/| |_ | (_| || || |\__ \ _ _ _ 90 | // \__,_| \___| \__| \__,_||_||_||___/(_)(_)(_) 91 | // 92 | // Code beyond this point is implementation detail... 93 | // 94 | //============================================================================== 95 | 96 | template 97 | ThreadSafeFunctor::ThreadSafeFunctor() : callback (std::make_shared()) 98 | { 99 | } 100 | 101 | template 102 | ThreadSafeFunctor::ThreadSafeFunctor (FunctionType&& f) : ThreadSafeFunctor() 103 | { 104 | callback->fn = std::move (f); 105 | } 106 | 107 | template 108 | template 109 | bool ThreadSafeFunctor::operator() (Args&&... args) const 110 | { 111 | std::scoped_lock l (callback->lock); 112 | 113 | if (callback->fn) 114 | { 115 | callback->fn (std::forward (args)...); 116 | return true; 117 | } 118 | 119 | return false; 120 | } 121 | 122 | template 123 | ThreadSafeFunctor& ThreadSafeFunctor::operator= (FunctionType&& f) 124 | { 125 | std::scoped_lock l (callback->lock); 126 | callback->fn = std::move (f); 127 | return *this; 128 | } 129 | 130 | template 131 | ThreadSafeFunctor& ThreadSafeFunctor::operator= (const FunctionType& f) 132 | { 133 | std::scoped_lock l (callback->lock); 134 | callback->fn = f; 135 | return *this; 136 | } 137 | 138 | template 139 | ThreadSafeFunctor::operator bool() const 140 | { 141 | std::scoped_lock l (callback->lock); 142 | return callback->fn != nullptr; 143 | } 144 | 145 | template 146 | void ThreadSafeFunctor::reset() 147 | { 148 | std::scoped_lock l (callback->lock); 149 | callback->fn = {}; 150 | } 151 | 152 | } // namespace choc::threading 153 | 154 | #endif // CHOC_THREADSAFEFUNCTOR_HEADER_INCLUDED 155 | -------------------------------------------------------------------------------- /tidy_whitespace.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | 3 | # This little script just checks all the files in the choc repo for 4 | # any dodgy whitespace that might have snuck in. 5 | 6 | import os 7 | 8 | def stripfile(file): 9 | newContent = "" 10 | oldContent = "" 11 | 12 | with open (file, 'r') as fin: 13 | oldContent = fin.read() 14 | 15 | lines = oldContent.splitlines() 16 | 17 | while (len (lines) > 0 and lines[-1].rstrip() == ""): 18 | lines.pop() 19 | 20 | for l in lines: 21 | newContent += l.rstrip().replace("\t", " ") + "\n" 22 | 23 | if (newContent != oldContent): 24 | print ("Cleaning " + file) 25 | with open (file, "w") as fout: 26 | fout.write (newContent) 27 | 28 | def stripfolder(folder): 29 | print ("Checking " + folder) 30 | for root, dirs, files in os.walk (folder): 31 | for file in files: 32 | if (file.endswith (".cpp") or file.endswith (".h")): 33 | stripfile (os.path.join(root, file)) 34 | 35 | stripfolder (os.path.dirname (os.path.realpath(__file__))) 36 | --------------------------------------------------------------------------------