From 3b9b3d6e785ca73b051f0cad5a27eb435288d299 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sat, 21 Sep 2013 23:31:13 +0200 Subject: [PATCH] Update juce --- source/backend/plugin/CarlaPluginGui.hpp | 2 + .../buffers/juce_AudioDataConverters.cpp | 46 +- .../buffers/juce_AudioSampleBuffer.cpp | 36 +- .../buffers/juce_AudioSampleBuffer.h | 18 +- .../buffers/juce_FloatVectorOperations.cpp | 10 - .../effects/juce_IIRFilterOld.cpp | 239 ++++++++ .../effects/juce_IIRFilterOld.h | 148 +++++ .../juce_audio_basics/juce_audio_basics.cpp | 1 + .../juce_audio_basics/juce_audio_basics.h | 1 + .../juce_audio_basics/juce_module_info | 2 +- .../midi/juce_MidiBuffer.cpp | 6 +- .../juce_audio_basics/midi/juce_MidiBuffer.h | 6 +- .../juce_audio_basics/midi/juce_MidiFile.cpp | 58 +- .../juce_audio_basics/midi/juce_MidiFile.h | 22 +- .../midi/juce_MidiMessage.cpp | 7 +- .../juce_audio_basics/midi/juce_MidiMessage.h | 35 +- .../midi/juce_MidiMessageSequence.cpp | 5 +- .../sources/juce_BufferingAudioSource.cpp | 34 +- .../sources/juce_ToneGeneratorAudioSource.cpp | 5 +- .../synthesisers/juce_Synthesiser.cpp | 40 +- .../synthesisers/juce_Synthesiser.h | 52 +- source/modules/juce_core/AppConfig.h | 21 +- .../containers/juce_AbstractFifo.cpp | 13 +- .../modules/juce_core/containers/juce_Array.h | 32 +- .../juce_core/containers/juce_PropertySet.cpp | 18 +- .../juce_core/containers/juce_PropertySet.h | 18 +- source/modules/juce_core/files/juce_File.cpp | 83 +-- source/modules/juce_core/files/juce_File.h | 12 +- source/modules/juce_core/json/juce_JSON.cpp | 25 +- source/modules/juce_core/json/juce_JSON.h | 3 + source/modules/juce_core/juce_core.h | 4 +- source/modules/juce_core/juce_module_info | 2 +- .../juce_core/maths/juce_BigInteger.cpp | 4 +- .../modules/juce_core/maths/juce_BigInteger.h | 2 +- .../modules/juce_core/maths/juce_Random.cpp | 36 +- source/modules/juce_core/maths/juce_Random.h | 11 +- .../juce_core/memory/juce_MemoryBlock.cpp | 24 +- .../juce_core/memory/juce_MemoryBlock.h | 4 +- source/modules/juce_core/misc/juce_Uuid.cpp | 30 +- source/modules/juce_core/misc/juce_Uuid.h | 13 +- .../native/juce_android_SystemStats.cpp | 28 +- .../juce_core/native/juce_linux_Files.cpp | 17 +- .../native/juce_linux_SystemStats.cpp | 10 +- .../juce_core/native/juce_mac_Files.mm | 4 +- .../juce_core/native/juce_mac_Network.mm | 36 +- .../juce_core/native/juce_posix_SharedCode.h | 27 +- .../juce_core/native/juce_win32_Network.cpp | 2 +- .../juce_core/native/juce_win32_Registry.cpp | 2 +- .../juce_core/native/juce_win32_Threads.cpp | 71 +-- source/modules/juce_core/network/juce_URL.cpp | 29 +- source/modules/juce_core/network/juce_URL.h | 2 +- .../streams/juce_MemoryInputStream.cpp | 7 +- .../juce_core/system/juce_PlatformDefs.h | 6 +- .../juce_core/system/juce_StandardHeader.h | 2 +- .../juce_core/text/juce_CharacterFunctions.h | 20 + .../modules/juce_core/text/juce_Identifier.h | 5 +- source/modules/juce_core/text/juce_NewLine.h | 8 + source/modules/juce_core/text/juce_String.cpp | 542 ++++++++---------- source/modules/juce_core/text/juce_String.h | 83 +-- .../juce_core/text/juce_StringArray.cpp | 118 ++-- .../modules/juce_core/text/juce_StringArray.h | 45 +- .../juce_core/text/juce_StringPairArray.cpp | 6 +- .../juce_core/text/juce_StringPairArray.h | 12 +- .../juce_core/text/juce_StringPool.cpp | 41 +- .../modules/juce_core/text/juce_StringRef.h | 135 +++++ .../modules/juce_core/text/juce_TextDiff.cpp | 11 +- .../juce_core/threads/juce_CriticalSection.h | 12 +- .../modules/juce_core/threads/juce_Process.h | 4 +- .../juce_core/threads/juce_ReadWriteLock.h | 11 +- .../juce_core/threads/juce_WaitableEvent.h | 3 +- .../juce_core/unit_tests/juce_UnitTest.cpp | 39 +- .../juce_core/unit_tests/juce_UnitTest.h | 27 +- .../juce_core/xml/juce_XmlDocument.cpp | 6 +- .../modules/juce_core/xml/juce_XmlElement.cpp | 135 +++-- .../modules/juce_core/xml/juce_XmlElement.h | 71 +-- .../zip/juce_GZIPCompressorOutputStream.cpp | 2 +- .../zip/juce_GZIPDecompressorInputStream.cpp | 4 + 77 files changed, 1564 insertions(+), 1147 deletions(-) create mode 100644 source/modules/juce_audio_basics/effects/juce_IIRFilterOld.cpp create mode 100644 source/modules/juce_audio_basics/effects/juce_IIRFilterOld.h create mode 100644 source/modules/juce_core/text/juce_StringRef.h diff --git a/source/backend/plugin/CarlaPluginGui.hpp b/source/backend/plugin/CarlaPluginGui.hpp index b9982eb5f..c444d3259 100644 --- a/source/backend/plugin/CarlaPluginGui.hpp +++ b/source/backend/plugin/CarlaPluginGui.hpp @@ -20,6 +20,8 @@ #include "CarlaPluginInternal.hpp" +#include "juce_gui_basics.h" + #if 0 #include diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp index 9505682ce..95d228e30 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_AudioDataConverters.cpp @@ -482,17 +482,16 @@ public: template struct Test5 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - test (unitTest, false); - test (unitTest, true); + test (unitTest, false, r); + test (unitTest, true, r); } - static void test (UnitTest& unitTest, bool inPlace) + static void test (UnitTest& unitTest, bool inPlace, Random& r) { const int numSamples = 2048; int32 original [numSamples], converted [numSamples], reversed [numSamples]; - Random r; { AudioData::Pointer d (original); @@ -549,49 +548,50 @@ public: template struct Test3 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - Test5 ::test (unitTest); - Test5 ::test (unitTest); + Test5 ::test (unitTest, r); + Test5 ::test (unitTest, r); } }; template struct Test2 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); - Test3 ::test (unitTest); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); + Test3 ::test (unitTest, r); } }; template struct Test1 { - static void test (UnitTest& unitTest) + static void test (UnitTest& unitTest, Random& r) { - Test2 ::test (unitTest); - Test2 ::test (unitTest); + Test2 ::test (unitTest, r); + Test2 ::test (unitTest, r); } }; void runTest() { + Random r = getRandom(); beginTest ("Round-trip conversion: Int8"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int16"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int24"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Int32"); - Test1 ::test (*this); + Test1 ::test (*this, r); beginTest ("Round-trip conversion: Float32"); - Test1 ::test (*this); + Test1 ::test (*this, r); } }; diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp index 1dd3e3c28..944140f71 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.cpp @@ -285,9 +285,7 @@ void AudioSampleBuffer::applyGainRamp (const int channel, } } -void AudioSampleBuffer::applyGain (const int startSample, - const int numSamples, - const float gain) noexcept +void AudioSampleBuffer::applyGain (int startSample, int numSamples, float gain) noexcept { for (int i = 0; i < numChannels; ++i) applyGain (i, startSample, numSamples, gain); @@ -298,10 +296,8 @@ void AudioSampleBuffer::applyGain (const float gain) noexcept applyGain (0, size, gain); } -void AudioSampleBuffer::applyGainRamp (const int startSample, - const int numSamples, - const float startGain, - const float endGain) noexcept +void AudioSampleBuffer::applyGainRamp (int startSample, int numSamples, + float startGain, float endGain) noexcept { for (int i = 0; i < numChannels; ++i) applyGainRamp (i, startSample, numSamples, startGain, endGain); @@ -399,11 +395,9 @@ void AudioSampleBuffer::copyFrom (const int destChannel, jassert (sourceStartSample >= 0 && sourceStartSample + numSamples <= source.size); if (numSamples > 0) - { FloatVectorOperations::copy (channels [destChannel] + destStartSample, source.channels [sourceChannel] + sourceStartSample, numSamples); - } } void AudioSampleBuffer::copyFrom (const int destChannel, @@ -416,11 +410,7 @@ void AudioSampleBuffer::copyFrom (const int destChannel, jassert (source != nullptr); if (numSamples > 0) - { - FloatVectorOperations::copy (channels [destChannel] + destStartSample, - source, - numSamples); - } + FloatVectorOperations::copy (channels [destChannel] + destStartSample, source, numSamples); } void AudioSampleBuffer::copyFrom (const int destChannel, @@ -482,6 +472,21 @@ void AudioSampleBuffer::copyFromWithRamp (const int destChannel, } } +void AudioSampleBuffer::reverse (int channel, int startSample, int numSamples) const noexcept +{ + jassert (isPositiveAndBelow (channel, numChannels)); + jassert (startSample >= 0 && startSample + numSamples <= size); + + std::reverse (channels[channel] + startSample, + channels[channel] + startSample + numSamples); +} + +void AudioSampleBuffer::reverse (int startSample, int numSamples) const noexcept +{ + for (int i = 0; i < numChannels; ++i) + reverse (i, startSample, numSamples); +} + void AudioSampleBuffer::findMinMax (const int channel, const int startSample, int numSamples, @@ -508,8 +513,7 @@ float AudioSampleBuffer::getMagnitude (const int channel, return jmax (mn, -mn, mx, -mx); } -float AudioSampleBuffer::getMagnitude (const int startSample, - const int numSamples) const noexcept +float AudioSampleBuffer::getMagnitude (int startSample, int numSamples) const noexcept { float mag = 0.0f; diff --git a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h index bff2a0a7c..cd71c3da2 100644 --- a/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h +++ b/source/modules/juce_audio_basics/buffers/juce_AudioSampleBuffer.h @@ -107,19 +107,16 @@ public: //============================================================================== /** Returns the number of channels of audio data that this buffer contains. - @see getSampleData */ int getNumChannels() const noexcept { return numChannels; } /** Returns the number of samples allocated in each of the buffer's channels. - @see getSampleData */ int getNumSamples() const noexcept { return size; } /** Returns a pointer one of the buffer's channels. - For speed, this doesn't check whether the channel number is out of range, so be careful when using it! */ @@ -409,23 +406,26 @@ public: float& minVal, float& maxVal) const noexcept; - /** Finds the highest absolute sample value within a region of a channel. - */ + /** Finds the highest absolute sample value within a region of a channel. */ float getMagnitude (int channel, int startSample, int numSamples) const noexcept; - /** Finds the highest absolute sample value within a region on all channels. - */ + /** Finds the highest absolute sample value within a region on all channels. */ float getMagnitude (int startSample, int numSamples) const noexcept; - /** Returns the root mean squared level for a region of a channel. - */ + /** Returns the root mean squared level for a region of a channel. */ float getRMSLevel (int channel, int startSample, int numSamples) const noexcept; + /** Reverses a part of a channel. */ + void reverse (int channel, int startSample, int numSamples) const noexcept; + + /** Reverses a part of the buffer. */ + void reverse (int startSample, int numSamples) const noexcept; + private: //============================================================================== int numChannels, size; diff --git a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp index d863ba931..53dd00b55 100644 --- a/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp +++ b/source/modules/juce_audio_basics/buffers/juce_FloatVectorOperations.cpp @@ -42,13 +42,6 @@ namespace FloatVectorHelpers return (((pointer_sized_int) p) & 15) == 0; } - inline static void mmEmpty() noexcept - { - #if ! JUCE_64BIT - _mm_empty(); - #endif - } - static inline float findMinimumOrMaximum (const float* src, int num, const bool isMinimum) noexcept { #if JUCE_USE_SSE_INTRINSICS @@ -84,7 +77,6 @@ namespace FloatVectorHelpers { float vals[4]; _mm_storeu_ps (vals, val); - FloatVectorHelpers::mmEmpty(); localVal = isMinimum ? jmin (vals[0], vals[1], vals[2], vals[3]) : jmax (vals[0], vals[1], vals[2], vals[3]); @@ -111,7 +103,6 @@ namespace FloatVectorHelpers const int numLongOps = num / 4; #define JUCE_FINISH_SSE_OP(normalOp) \ - FloatVectorHelpers::mmEmpty(); \ num &= 3; \ if (num == 0) return; \ } \ @@ -301,7 +292,6 @@ void JUCE_CALLTYPE FloatVectorOperations::findMinAndMax (const float* src, int n float mns[4], mxs[4]; _mm_storeu_ps (mns, mn); _mm_storeu_ps (mxs, mx); - FloatVectorHelpers::mmEmpty(); localMin = jmin (mns[0], mns[1], mns[2], mns[3]); localMax = jmax (mxs[0], mxs[1], mxs[2], mxs[3]); diff --git a/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.cpp b/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.cpp new file mode 100644 index 000000000..25ab4f14e --- /dev/null +++ b/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.cpp @@ -0,0 +1,239 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#if JUCE_INTEL + #define JUCE_SNAP_TO_ZERO(n) if (! (n < -1.0e-8 || n > 1.0e-8)) n = 0; +#else + #define JUCE_SNAP_TO_ZERO(n) +#endif + +//============================================================================== +IIRFilterOld::IIRFilterOld() + : active (false), v1 (0), v2 (0) +{ + zeromem (coefficients, sizeof (coefficients)); +} + +IIRFilterOld::IIRFilterOld (const IIRFilterOld& other) + : active (other.active), v1 (0), v2 (0) +{ + const SpinLock::ScopedLockType sl (other.processLock); + memcpy (coefficients, other.coefficients, sizeof (coefficients)); +} + +IIRFilterOld::~IIRFilterOld() +{ +} + +//============================================================================== +void IIRFilterOld::reset() noexcept +{ + const SpinLock::ScopedLockType sl (processLock); + v1 = v2 = 0; +} + +float IIRFilterOld::processSingleSampleRaw (const float in) noexcept +{ + float out = coefficients[0] * in + v1; + + JUCE_SNAP_TO_ZERO (out); + + v1 = coefficients[1] * in - coefficients[3] * out + v2; + v2 = coefficients[2] * in - coefficients[4] * out; + + return out; +} + +void IIRFilterOld::processSamples (float* const samples, + const int numSamples) noexcept +{ + const SpinLock::ScopedLockType sl (processLock); + + if (active) + { + const float c0 = coefficients[0]; + const float c1 = coefficients[1]; + const float c2 = coefficients[2]; + const float c3 = coefficients[3]; + const float c4 = coefficients[4]; + float lv1 = v1, lv2 = v2; + + for (int i = 0; i < numSamples; ++i) + { + const float in = samples[i]; + const float out = c0 * in + lv1; + samples[i] = out; + + lv1 = c1 * in - c3 * out + lv2; + lv2 = c2 * in - c4 * out; + } + + JUCE_SNAP_TO_ZERO (lv1); v1 = lv1; + JUCE_SNAP_TO_ZERO (lv2); v2 = lv2; + } +} + +//============================================================================== +void IIRFilterOld::makeLowPass (const double sampleRate, + const double frequency) noexcept +{ + jassert (sampleRate > 0); + + const double n = 1.0 / tan (double_Pi * frequency / sampleRate); + const double nSquared = n * n; + const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); + + setCoefficients (c1, + c1 * 2.0f, + c1, + 1.0, + c1 * 2.0 * (1.0 - nSquared), + c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); +} + +void IIRFilterOld::makeHighPass (const double sampleRate, + const double frequency) noexcept +{ + const double n = tan (double_Pi * frequency / sampleRate); + const double nSquared = n * n; + const double c1 = 1.0 / (1.0 + std::sqrt (2.0) * n + nSquared); + + setCoefficients (c1, + c1 * -2.0f, + c1, + 1.0, + c1 * 2.0 * (nSquared - 1.0), + c1 * (1.0 - std::sqrt (2.0) * n + nSquared)); +} + +void IIRFilterOld::makeLowShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) noexcept +{ + jassert (sampleRate > 0); + jassert (Q > 0); + + const double A = jmax (0.0f, gainFactor); + const double aminus1 = A - 1.0; + const double aplus1 = A + 1.0; + const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; + const double coso = std::cos (omega); + const double beta = std::sin (omega) * std::sqrt (A) / Q; + const double aminus1TimesCoso = aminus1 * coso; + + setCoefficients (A * (aplus1 - aminus1TimesCoso + beta), + A * 2.0 * (aminus1 - aplus1 * coso), + A * (aplus1 - aminus1TimesCoso - beta), + aplus1 + aminus1TimesCoso + beta, + -2.0 * (aminus1 + aplus1 * coso), + aplus1 + aminus1TimesCoso - beta); +} + +void IIRFilterOld::makeHighShelf (const double sampleRate, + const double cutOffFrequency, + const double Q, + const float gainFactor) noexcept +{ + jassert (sampleRate > 0); + jassert (Q > 0); + + const double A = jmax (0.0f, gainFactor); + const double aminus1 = A - 1.0; + const double aplus1 = A + 1.0; + const double omega = (double_Pi * 2.0 * jmax (cutOffFrequency, 2.0)) / sampleRate; + const double coso = std::cos (omega); + const double beta = std::sin (omega) * std::sqrt (A) / Q; + const double aminus1TimesCoso = aminus1 * coso; + + setCoefficients (A * (aplus1 + aminus1TimesCoso + beta), + A * -2.0 * (aminus1 + aplus1 * coso), + A * (aplus1 + aminus1TimesCoso - beta), + aplus1 - aminus1TimesCoso + beta, + 2.0 * (aminus1 - aplus1 * coso), + aplus1 - aminus1TimesCoso - beta); +} + +void IIRFilterOld::makeBandPass (const double sampleRate, + const double centreFrequency, + const double Q, + const float gainFactor) noexcept +{ + jassert (sampleRate > 0); + jassert (Q > 0); + + const double A = jmax (0.0f, gainFactor); + const double omega = (double_Pi * 2.0 * jmax (centreFrequency, 2.0)) / sampleRate; + const double alpha = 0.5 * std::sin (omega) / Q; + const double c2 = -2.0 * std::cos (omega); + const double alphaTimesA = alpha * A; + const double alphaOverA = alpha / A; + + setCoefficients (1.0 + alphaTimesA, + c2, + 1.0 - alphaTimesA, + 1.0 + alphaOverA, + c2, + 1.0 - alphaOverA); +} + +void IIRFilterOld::makeInactive() noexcept +{ + const SpinLock::ScopedLockType sl (processLock); + active = false; +} + +//============================================================================== +void IIRFilterOld::copyCoefficientsFrom (const IIRFilterOld& other) noexcept +{ + const SpinLock::ScopedLockType sl (processLock); + + memcpy (coefficients, other.coefficients, sizeof (coefficients)); + active = other.active; +} + +//============================================================================== +void IIRFilterOld::setCoefficients (double c1, double c2, double c3, + double c4, double c5, double c6) noexcept +{ + const double a = 1.0 / c4; + + c1 *= a; + c2 *= a; + c3 *= a; + c5 *= a; + c6 *= a; + + const SpinLock::ScopedLockType sl (processLock); + + coefficients[0] = (float) c1; + coefficients[1] = (float) c2; + coefficients[2] = (float) c3; + coefficients[3] = (float) c5; + coefficients[4] = (float) c6; + + active = true; +} + +#undef JUCE_SNAP_TO_ZERO diff --git a/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.h b/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.h new file mode 100644 index 000000000..8c929c584 --- /dev/null +++ b/source/modules/juce_audio_basics/effects/juce_IIRFilterOld.h @@ -0,0 +1,148 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + Permission is granted to use this software under the terms of either: + a) the GPL v2 (or any later version) + b) the Affero GPL v3 + + Details of these licenses can be found at: www.gnu.org/licenses + + JUCE is distributed in the hope that it will be useful, but WITHOUT ANY + WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + ------------------------------------------------------------------------------ + + To release a closed-source product which uses JUCE, commercial licenses are + available: visit www.juce.com for more information. + + ============================================================================== +*/ + +#ifndef __JUCE_IIRFILTER_OLD_JUCEHEADER__ +#define __JUCE_IIRFILTER_OLD_JUCEHEADER__ + + +//============================================================================== +/** + An IIR filter that can perform low, high, or band-pass filtering on an + audio signal. + + @see IIRFilterAudioSource +*/ +class JUCE_API IIRFilterOld +{ +public: + //============================================================================== + /** Creates a filter. + + Initially the filter is inactive, so will have no effect on samples that + you process with it. Use the appropriate method to turn it into the type + of filter needed. + */ + IIRFilterOld(); + + /** Creates a copy of another filter. */ + IIRFilterOld (const IIRFilterOld& other); + + /** Destructor. */ + ~IIRFilterOld(); + + //============================================================================== + /** Resets the filter's processing pipeline, ready to start a new stream of data. + + Note that this clears the processing state, but the type of filter and + its coefficients aren't changed. To put a filter into an inactive state, use + the makeInactive() method. + */ + void reset() noexcept; + + /** Performs the filter operation on the given set of samples. + */ + void processSamples (float* samples, + int numSamples) noexcept; + + /** Processes a single sample, without any locking or checking. + + Use this if you need fast processing of a single value, but be aware that + this isn't thread-safe in the way that processSamples() is. + */ + float processSingleSampleRaw (float sample) noexcept; + + //============================================================================== + /** Sets the filter up to act as a low-pass filter. + */ + void makeLowPass (double sampleRate, + double frequency) noexcept; + + /** Sets the filter up to act as a high-pass filter. + */ + void makeHighPass (double sampleRate, + double frequency) noexcept; + + //============================================================================== + /** Sets the filter up to act as a low-pass shelf filter with variable Q and gain. + + The gain is a scale factor that the low frequencies are multiplied by, so values + greater than 1.0 will boost the low frequencies, values less than 1.0 will + attenuate them. + */ + void makeLowShelf (double sampleRate, + double cutOffFrequency, + double Q, + float gainFactor) noexcept; + + /** Sets the filter up to act as a high-pass shelf filter with variable Q and gain. + + The gain is a scale factor that the high frequencies are multiplied by, so values + greater than 1.0 will boost the high frequencies, values less than 1.0 will + attenuate them. + */ + void makeHighShelf (double sampleRate, + double cutOffFrequency, + double Q, + float gainFactor) noexcept; + + /** Sets the filter up to act as a band pass filter centred around a + frequency, with a variable Q and gain. + + The gain is a scale factor that the centre frequencies are multiplied by, so + values greater than 1.0 will boost the centre frequencies, values less than + 1.0 will attenuate them. + */ + void makeBandPass (double sampleRate, + double centreFrequency, + double Q, + float gainFactor) noexcept; + + /** Clears the filter's coefficients so that it becomes inactive. + */ + void makeInactive() noexcept; + + //============================================================================== + /** Makes this filter duplicate the set-up of another one. + */ + void copyCoefficientsFrom (const IIRFilterOld& other) noexcept; + + +protected: + //============================================================================== + SpinLock processLock; + + void setCoefficients (double c1, double c2, double c3, + double c4, double c5, double c6) noexcept; + + bool active; + float coefficients[5]; + float v1, v2; + + // (use the copyCoefficientsFrom() method instead of this operator) + IIRFilterOld& operator= (const IIRFilterOld&); + JUCE_LEAK_DETECTOR (IIRFilterOld) +}; + + +#endif // __JUCE_IIRFILTER_OLD_JUCEHEADER__ diff --git a/source/modules/juce_audio_basics/juce_audio_basics.cpp b/source/modules/juce_audio_basics/juce_audio_basics.cpp index 06ed681d4..2bdd5a490 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.cpp +++ b/source/modules/juce_audio_basics/juce_audio_basics.cpp @@ -65,6 +65,7 @@ namespace juce #include "buffers/juce_AudioSampleBuffer.cpp" #include "buffers/juce_FloatVectorOperations.cpp" #include "effects/juce_IIRFilter.cpp" +#include "effects/juce_IIRFilterOld.cpp" #include "effects/juce_LagrangeInterpolator.cpp" #include "midi/juce_MidiBuffer.cpp" #include "midi/juce_MidiFile.cpp" diff --git a/source/modules/juce_audio_basics/juce_audio_basics.h b/source/modules/juce_audio_basics/juce_audio_basics.h index 70c5432a7..c9ce9abb2 100644 --- a/source/modules/juce_audio_basics/juce_audio_basics.h +++ b/source/modules/juce_audio_basics/juce_audio_basics.h @@ -36,6 +36,7 @@ namespace juce #include "buffers/juce_FloatVectorOperations.h" #include "effects/juce_Decibels.h" #include "effects/juce_IIRFilter.h" +#include "effects/juce_IIRFilterOld.h" #include "effects/juce_LagrangeInterpolator.h" #include "effects/juce_Reverb.h" #include "midi/juce_MidiMessage.h" diff --git a/source/modules/juce_audio_basics/juce_module_info b/source/modules/juce_audio_basics/juce_module_info index 20caa3fc3..1a82c6fba 100644 --- a/source/modules/juce_audio_basics/juce_module_info +++ b/source/modules/juce_audio_basics/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_audio_basics", "name": "JUCE audio and midi data classes", - "version": "2.1.2", + "version": "2.1.3", "description": "Classes for audio buffer manipulation, midi message handling, synthesis, etc", "website": "http://www.juce.com/juce", "license": "GPL/Commercial", diff --git a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp index c679c91e2..58d5e0a7c 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.cpp @@ -240,9 +240,8 @@ uint8* MidiBuffer::findEventAfter (uint8* d, const int samplePosition) const noe } //============================================================================== -MidiBuffer::Iterator::Iterator (const MidiBuffer& buffer_) noexcept - : buffer (buffer_), - data (buffer_.getData()) +MidiBuffer::Iterator::Iterator (const MidiBuffer& b) noexcept + : buffer (b), data (b.getData()) { } @@ -250,7 +249,6 @@ MidiBuffer::Iterator::~Iterator() noexcept { } -//============================================================================== void MidiBuffer::Iterator::setNextSamplePosition (const int samplePosition) noexcept { data = buffer.getData(); diff --git a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h index 7c8e51c73..d60305e0b 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiBuffer.h @@ -51,10 +51,10 @@ public: explicit MidiBuffer (const MidiMessage& message) noexcept; /** Creates a copy of another MidiBuffer. */ - MidiBuffer (const MidiBuffer& other) noexcept; + MidiBuffer (const MidiBuffer&) noexcept; /** Makes a copy of another MidiBuffer. */ - MidiBuffer& operator= (const MidiBuffer& other) noexcept; + MidiBuffer& operator= (const MidiBuffer&) noexcept; /** Destructor */ ~MidiBuffer(); @@ -176,7 +176,7 @@ public: public: //============================================================================== /** Creates an Iterator for this MidiBuffer. */ - Iterator (const MidiBuffer& buffer) noexcept; + Iterator (const MidiBuffer&) noexcept; /** Destructor. */ ~Iterator() noexcept; diff --git a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp b/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp index adeb4fff7..238bc4f9c 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiFile.cpp @@ -146,6 +146,26 @@ namespace MidiFileHelpers return 0; } }; + + template + static void findAllMatchingEvents (const OwnedArray& tracks, + MidiMessageSequence& results, + MethodType method) + { + for (int i = 0; i < tracks.size(); ++i) + { + const MidiMessageSequence& track = *tracks.getUnchecked(i); + const int numEvents = track.getNumEvents(); + + for (int j = 0; j < numEvents; ++j) + { + const MidiMessage& m = track.getEventPointer(j)->message; + + if ((m.*method)()) + results.addEvent (m); + } + } + } } //============================================================================== @@ -197,36 +217,19 @@ void MidiFile::setSmpteTimeFormat (const int framesPerSecond, } //============================================================================== -void MidiFile::findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const +void MidiFile::findAllTempoEvents (MidiMessageSequence& results) const { - for (int i = tracks.size(); --i >= 0;) - { - const int numEvents = tracks.getUnchecked(i)->getNumEvents(); - - for (int j = 0; j < numEvents; ++j) - { - const MidiMessage& m = tracks.getUnchecked(i)->getEventPointer (j)->message; - - if (m.isTempoMetaEvent()) - tempoChangeEvents.addEvent (m); - } - } + MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTempoMetaEvent); } -void MidiFile::findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const +void MidiFile::findAllTimeSigEvents (MidiMessageSequence& results) const { - for (int i = tracks.size(); --i >= 0;) - { - const int numEvents = tracks.getUnchecked(i)->getNumEvents(); - - for (int j = 0; j < numEvents; ++j) - { - const MidiMessage& m = tracks.getUnchecked(i)->getEventPointer (j)->message; + MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isTimeSignatureMetaEvent); +} - if (m.isTimeSignatureMetaEvent()) - timeSigEvents.addEvent (m); - } - } +void MidiFile::findAllKeySigEvents (MidiMessageSequence& results) const +{ + MidiFileHelpers::findAllMatchingEvents (tracks, results, &MidiMessage::isKeySignatureMetaEvent); } double MidiFile::getLastTimestamp() const @@ -340,10 +343,7 @@ void MidiFile::convertTimestampTicksToSeconds() for (int j = ms.getNumEvents(); --j >= 0;) { MidiMessage& m = ms.getEventPointer(j)->message; - - m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), - tempoEvents, - timeFormat)); + m.setTimeStamp (MidiFileHelpers::convertTicksToSeconds (m.getTimeStamp(), tempoEvents, timeFormat)); } } } diff --git a/source/modules/juce_audio_basics/midi/juce_MidiFile.h b/source/modules/juce_audio_basics/midi/juce_MidiFile.h index 53476a87e..6bdc784ec 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiFile.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiFile.h @@ -52,28 +52,23 @@ public: //============================================================================== /** Returns the number of tracks in the file. - @see getTrack, addTrack */ int getNumTracks() const noexcept; /** Returns a pointer to one of the tracks in the file. - @returns a pointer to the track, or nullptr if the index is out-of-range @see getNumTracks, addTrack */ const MidiMessageSequence* getTrack (int index) const noexcept; /** Adds a midi track to the file. - This will make its own internal copy of the sequence that is passed-in. - @see getNumTracks, getTrack */ void addTrack (const MidiMessageSequence& trackSequence); /** Removes all midi tracks from the file. - @see getNumTracks */ void clear(); @@ -120,23 +115,23 @@ public: //============================================================================== /** Makes a list of all the tempo-change meta-events from all tracks in the midi file. - Useful for finding the positions of all the tempo changes in a file. - @param tempoChangeEvents a list to which all the events will be added */ void findAllTempoEvents (MidiMessageSequence& tempoChangeEvents) const; /** Makes a list of all the time-signature meta-events from all tracks in the midi file. - Useful for finding the positions of all the tempo changes in a file. - @param timeSigEvents a list to which all the events will be added */ void findAllTimeSigEvents (MidiMessageSequence& timeSigEvents) const; - /** Returns the latest timestamp in any of the tracks. + /** Makes a list of all the time-signature meta-events from all tracks in the midi file. + @param keySigEvents a list to which all the events will be added + */ + void findAllKeySigEvents (MidiMessageSequence& keySigEvents) const; + /** Returns the latest timestamp in any of the tracks. (Useful for finding the length of the file). */ double getLastTimestamp() const; @@ -156,7 +151,6 @@ public: bool readFrom (InputStream& sourceStream); /** Writes the midi tracks as a standard midi file. - @returns true if the operation succeeded. */ bool writeTo (OutputStream& destStream); @@ -171,11 +165,11 @@ public: private: //============================================================================== - OwnedArray tracks; + OwnedArray tracks; short timeFormat; - void readNextTrack (const uint8* data, int size); - void writeTrack (OutputStream& mainOut, int trackNum); + void readNextTrack (const uint8*, int size); + void writeTrack (OutputStream&, int trackNum); JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MidiFile) }; diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp index 7091eb771..5657a4436 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.cpp @@ -797,7 +797,12 @@ bool MidiMessage::isKeySignatureMetaEvent() const noexcept int MidiMessage::getKeySignatureNumberOfSharpsOrFlats() const noexcept { - return (int) *getMetaEventData(); + return (int) getMetaEventData()[0]; +} + +bool MidiMessage::isKeySignatureMajorKey() const noexcept +{ + return getMetaEventData()[1] == 0; } MidiMessage MidiMessage::endOfTrack() noexcept diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h b/source/modules/juce_audio_basics/midi/juce_MidiMessage.h index 40bce10c7..6582a9cbd 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessage.h +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessage.h @@ -112,13 +112,11 @@ public: //============================================================================== /** Returns a pointer to the raw midi data. - @see getRawDataSize */ const uint8* getRawData() const noexcept { return data; } /** Returns the number of bytes of data in the message. - @see getRawData */ int getRawDataSize() const noexcept { return size; } @@ -143,15 +141,12 @@ public: double getTimeStamp() const noexcept { return timeStamp; } /** Changes the message's associated timestamp. - The units for the timestamp will be application-specific - see the notes for getTimeStamp(). - @see addToTimeStamp, getTimeStamp */ void setTimeStamp (double newTimestamp) noexcept { timeStamp = newTimestamp; } /** Adds a value to the message's timestamp. - The units for the timestamp will be application-specific. */ void addToTimeStamp (double delta) noexcept { timeStamp += delta; } @@ -569,7 +564,6 @@ public: //============================================================================== /** Returns true if this is a 'tempo' meta-event. - @see getTempoMetaEventTickLength, getTempoSecondsPerQuarterNote */ bool isTempoMetaEvent() const noexcept; @@ -583,49 +577,51 @@ public: double getTempoMetaEventTickLength (short timeFormat) const noexcept; /** Calculates the seconds-per-quarter-note from a tempo meta-event. - @see isTempoMetaEvent, getTempoMetaEventTickLength */ double getTempoSecondsPerQuarterNote() const noexcept; /** Creates a tempo meta-event. - @see isTempoMetaEvent */ static MidiMessage tempoMetaEvent (int microsecondsPerQuarterNote) noexcept; //============================================================================== /** Returns true if this is a 'time-signature' meta-event. - @see getTimeSignatureInfo */ bool isTimeSignatureMetaEvent() const noexcept; /** Returns the time-signature values from a time-signature meta-event. - @see isTimeSignatureMetaEvent */ void getTimeSignatureInfo (int& numerator, int& denominator) const noexcept; /** Creates a time-signature meta-event. - @see isTimeSignatureMetaEvent */ static MidiMessage timeSignatureMetaEvent (int numerator, int denominator); //============================================================================== /** Returns true if this is a 'key-signature' meta-event. - - @see getKeySignatureNumberOfSharpsOrFlats + @see getKeySignatureNumberOfSharpsOrFlats, isKeySignatureMajorKey */ bool isKeySignatureMetaEvent() const noexcept; /** Returns the key from a key-signature meta-event. - - @see isKeySignatureMetaEvent + This method must only be called if isKeySignatureMetaEvent() is true. + A positive number here indicates the number of sharps in the key signature, + and a negative number indicates a number of flats. So e.g. 3 = F# + C# + G#, + -2 = Bb + Eb + @see isKeySignatureMetaEvent, isKeySignatureMajorKey */ int getKeySignatureNumberOfSharpsOrFlats() const noexcept; + /** Returns true if this key-signature event is major, or false if it's minor. + This method must only be called if isKeySignatureMetaEvent() is true. + */ + bool isKeySignatureMajorKey() const noexcept; + //============================================================================== /** Returns true if this is a 'channel' meta-event. @@ -807,14 +803,11 @@ public: */ MidiMachineControlCommand getMidiMachineControlCommand() const noexcept; - /** Creates an MMC message. - */ + /** Creates an MMC message. */ static MidiMessage midiMachineControlCommand (MidiMachineControlCommand command); /** Checks whether this is an MMC "goto" message. - If it is, the parameters passed-in are set to the time that the message contains. - @see midiMachineControlGoto */ bool isMidiMachineControlGoto (int& hours, @@ -823,9 +816,7 @@ public: int& frames) const noexcept; /** Creates an MMC "goto" message. - This messages tells the device to go to a specific frame. - @see isMidiMachineControlGoto */ static MidiMessage midiMachineControlGoto (int hours, @@ -835,14 +826,12 @@ public: //============================================================================== /** Creates a master-volume change message. - @param volume the volume, 0 to 1.0 */ static MidiMessage masterVolume (float volume); //============================================================================== /** Creates a system-exclusive message. - The data passed in is wrapped with header and tail bytes of 0xf0 and 0xf7. */ static MidiMessage createSysExMessage (const void* sysexData, diff --git a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp index e319a00fa..ded0dce4f 100644 --- a/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp +++ b/source/modules/juce_audio_basics/midi/juce_MidiMessageSequence.cpp @@ -284,7 +284,7 @@ void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumbe { bool doneProg = false; bool donePitchWheel = false; - Array doneControllers; + Array doneControllers; doneControllers.ensureStorageAllocated (32); for (int i = list.size(); --i >= 0;) @@ -324,8 +324,7 @@ void MidiMessageSequence::createControllerUpdatesForTime (const int channelNumbe //============================================================================== MidiMessageSequence::MidiEventHolder::MidiEventHolder (const MidiMessage& mm) - : message (mm), - noteOffObject (nullptr) + : message (mm), noteOffObject (nullptr) { } diff --git a/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp index 61c0e29eb..827cd7be5 100644 --- a/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_BufferingAudioSource.cpp @@ -22,16 +22,16 @@ ============================================================================== */ -BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_, - TimeSliceThread& backgroundThread_, +BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* s, + TimeSliceThread& thread, const bool deleteSourceWhenDeleted, - const int numberOfSamplesToBuffer_, - const int numberOfChannels_) - : source (source_, deleteSourceWhenDeleted), - backgroundThread (backgroundThread_), - numberOfSamplesToBuffer (jmax (1024, numberOfSamplesToBuffer_)), - numberOfChannels (numberOfChannels_), - buffer (numberOfChannels_, 0), + const int bufferSizeSamples, + const int numChannels) + : source (s, deleteSourceWhenDeleted), + backgroundThread (thread), + numberOfSamplesToBuffer (jmax (1024, bufferSizeSamples)), + numberOfChannels (numChannels), + buffer (numChannels, 0), bufferValidStart (0), bufferValidEnd (0), nextPlayPos (0), @@ -39,10 +39,10 @@ BufferingAudioSource::BufferingAudioSource (PositionableAudioSource* source_, wasSourceLooping (false), isPrepared (false) { - jassert (source_ != nullptr); + jassert (source != nullptr); - jassert (numberOfSamplesToBuffer_ > 1024); // not much point using this class if you're - // not using a larger buffer.. + jassert (numberOfSamplesToBuffer > 1024); // not much point using this class if you're + // not using a larger buffer.. } BufferingAudioSource::~BufferingAudioSource() @@ -51,20 +51,20 @@ BufferingAudioSource::~BufferingAudioSource() } //============================================================================== -void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sampleRate_) +void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double newSampleRate) { const int bufferSizeNeeded = jmax (samplesPerBlockExpected * 2, numberOfSamplesToBuffer); - if (sampleRate_ != sampleRate + if (newSampleRate != sampleRate || bufferSizeNeeded != buffer.getNumSamples() || ! isPrepared) { backgroundThread.removeTimeSliceClient (this); isPrepared = true; - sampleRate = sampleRate_; + sampleRate = newSampleRate; - source->prepareToPlay (samplesPerBlockExpected, sampleRate_); + source->prepareToPlay (samplesPerBlockExpected, newSampleRate); buffer.setSize (numberOfChannels, bufferSizeNeeded); buffer.clear(); @@ -74,7 +74,7 @@ void BufferingAudioSource::prepareToPlay (int samplesPerBlockExpected, double sa backgroundThread.addTimeSliceClient (this); - while (bufferValidEnd - bufferValidStart < jmin (((int) sampleRate_) / 4, + while (bufferValidEnd - bufferValidStart < jmin (((int) newSampleRate) / 4, buffer.getNumSamples() / 2)) { backgroundThread.moveToFrontOfQueue (this); diff --git a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp b/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp index 7bf4c4dd9..7f5b9393f 100644 --- a/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp +++ b/source/modules/juce_audio_basics/sources/juce_ToneGeneratorAudioSource.cpp @@ -48,12 +48,11 @@ void ToneGeneratorAudioSource::setFrequency (const double newFrequencyHz) } //============================================================================== -void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, - double sampleRate_) +void ToneGeneratorAudioSource::prepareToPlay (int /*samplesPerBlockExpected*/, double rate) { currentPhase = 0.0; phasePerSample = 0.0; - sampleRate = sampleRate_; + sampleRate = rate; } void ToneGeneratorAudioSource::releaseResources() diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp index 5a8e0f84e..6af687ede 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.cpp @@ -22,13 +22,8 @@ ============================================================================== */ -SynthesiserSound::SynthesiserSound() -{ -} - -SynthesiserSound::~SynthesiserSound() -{ -} +SynthesiserSound::SynthesiserSound() {} +SynthesiserSound::~SynthesiserSound() {} //============================================================================== SynthesiserVoice::SynthesiserVoice() @@ -118,9 +113,9 @@ void Synthesiser::removeSound (const int index) sounds.remove (index); } -void Synthesiser::setNoteStealingEnabled (const bool shouldStealNotes_) +void Synthesiser::setNoteStealingEnabled (const bool shouldSteal) { - shouldStealNotes = shouldStealNotes_; + shouldStealNotes = shouldSteal; } //============================================================================== @@ -139,10 +134,8 @@ void Synthesiser::setCurrentPlaybackSampleRate (const double newRate) } } -void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, - const MidiBuffer& midiData, - int startSample, - int numSamples) +void Synthesiser::renderNextBlock (AudioSampleBuffer& outputBuffer, const MidiBuffer& midiData, + int startSample, int numSamples) { // must set the sample rate before using this! jassert (sampleRate != 0); @@ -180,15 +173,11 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) { if (m.isNoteOn()) { - noteOn (m.getChannel(), - m.getNoteNumber(), - m.getFloatVelocity()); + noteOn (m.getChannel(), m.getNoteNumber(), m.getFloatVelocity()); } else if (m.isNoteOff()) { - noteOff (m.getChannel(), - m.getNoteNumber(), - true); + noteOff (m.getChannel(), m.getNoteNumber(), true); } else if (m.isAllNotesOff() || m.isAllSoundOff()) { @@ -204,9 +193,7 @@ void Synthesiser::handleMidiEvent (const MidiMessage& m) } else if (m.isController()) { - handleController (m.getChannel(), - m.getControllerNumber(), - m.getControllerValue()); + handleController (m.getChannel(), m.getControllerNumber(), m.getControllerValue()); } } @@ -406,9 +393,12 @@ SynthesiserVoice* Synthesiser::findFreeVoice (SynthesiserSound* soundToPlay, const ScopedLock sl (lock); for (int i = voices.size(); --i >= 0;) - if (voices.getUnchecked (i)->getCurrentlyPlayingNote() < 0 - && voices.getUnchecked (i)->canPlaySound (soundToPlay)) - return voices.getUnchecked (i); + { + SynthesiserVoice* const voice = voices.getUnchecked (i); + + if (voice->getCurrentlyPlayingNote() < 0 && voice->canPlaySound (soundToPlay)) + return voice; + } if (stealIfNoneAvailable) { diff --git a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h index eac8ddd08..eb443cfd8 100644 --- a/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h +++ b/source/modules/juce_audio_basics/synthesisers/juce_Synthesiser.h @@ -64,9 +64,8 @@ public: */ virtual bool appliesToChannel (const int midiChannel) = 0; - /** - */ - typedef ReferenceCountedObjectPtr Ptr; + /** The class is reference-counted, so this is a handy pointer class for it. */ + typedef ReferenceCountedObjectPtr Ptr; private: @@ -96,16 +95,14 @@ public: //============================================================================== /** Returns the midi note that this voice is currently playing. - Returns a value less than 0 if no note is playing. */ - int getCurrentlyPlayingNote() const { return currentlyPlayingNote; } + int getCurrentlyPlayingNote() const noexcept { return currentlyPlayingNote; } /** Returns the sound that this voice is currently playing. - Returns nullptr if it's not playing. */ - SynthesiserSound::Ptr getCurrentlyPlayingSound() const { return currentlyPlayingSound; } + SynthesiserSound::Ptr getCurrentlyPlayingSound() const noexcept { return currentlyPlayingSound; } /** Must return true if this voice object is capable of playing the given sound. @@ -116,16 +113,16 @@ public: of voice and sound, or it might check the type of the sound object passed-in and see if it's one that it understands. */ - virtual bool canPlaySound (SynthesiserSound* sound) = 0; + virtual bool canPlaySound (SynthesiserSound*) = 0; /** Called to start a new note. This will be called during the rendering callback, so must be fast and thread-safe. */ - virtual void startNote (const int midiNoteNumber, - const float velocity, + virtual void startNote (int midiNoteNumber, + float velocity, SynthesiserSound* sound, - const int currentPitchWheelPosition) = 0; + int currentPitchWheelPosition) = 0; /** Called to stop a note. @@ -140,20 +137,17 @@ public: finishes playing (during the rendering callback), it must make sure that it calls clearCurrentNote(). */ - virtual void stopNote (const bool allowTailOff) = 0; + virtual void stopNote (bool allowTailOff) = 0; /** Called to let the voice know that the pitch wheel has been moved. - This will be called during the rendering callback, so must be fast and thread-safe. */ - virtual void pitchWheelMoved (const int newValue) = 0; + virtual void pitchWheelMoved (int newValue) = 0; /** Called to let the voice know that a midi controller has been moved. - This will be called during the rendering callback, so must be fast and thread-safe. */ - virtual void controllerMoved (const int controllerNumber, - const int newValue) = 0; + virtual void controllerMoved (int controllerNumber, int newValue) = 0; //============================================================================== /** Renders the next block of data for this voice. @@ -273,7 +267,7 @@ public: void clearVoices(); /** Returns the number of voices that have been added. */ - int getNumVoices() const { return voices.size(); } + int getNumVoices() const noexcept { return voices.size(); } /** Returns one of the voices that have been added. */ SynthesiserVoice* getVoice (int index) const; @@ -296,10 +290,10 @@ public: void clearSounds(); /** Returns the number of sounds that have been added to the synth. */ - int getNumSounds() const { return sounds.size(); } + int getNumSounds() const noexcept { return sounds.size(); } /** Returns one of the sounds. */ - SynthesiserSound* getSound (int index) const { return sounds [index]; } + SynthesiserSound* getSound (int index) const noexcept { return sounds [index]; } /** Adds a new sound to the synthesiser. @@ -323,7 +317,7 @@ public: /** Returns true if note-stealing is enabled. @see setNoteStealingEnabled */ - bool isNoteStealingEnabled() const { return shouldStealNotes; } + bool isNoteStealingEnabled() const noexcept { return shouldStealNotes; } //============================================================================== /** Triggers a note-on event. @@ -376,7 +370,7 @@ public: virtual void allNotesOff (int midiChannel, bool allowTailOff); - /** Sends a pitch-wheel message. + /** Sends a pitch-wheel message to any active voices. This will send a pitch-wheel message to any voices that are playing sounds on the given midi channel. @@ -390,7 +384,7 @@ public: virtual void handlePitchWheel (int midiChannel, int wheelValue); - /** Sends a midi controller message. + /** Sends a midi controller message to any active voices. This will send a midi controller message to any voices that are playing sounds on the given midi channel. @@ -406,13 +400,17 @@ public: int controllerNumber, int controllerValue); + /** Handles a sustain pedal event. */ virtual void handleSustainPedal (int midiChannel, bool isDown); + + /** Handles a sostenuto pedal event. */ virtual void handleSostenutoPedal (int midiChannel, bool isDown); + + /** Can be overridden to handle soft pedal events. */ virtual void handleSoftPedal (int midiChannel, bool isDown); //============================================================================== - /** Tells the synthesiser what the sample rate is for the audio it's being used to - render. + /** Tells the synthesiser what the sample rate is for the audio it's being used to render. This value is propagated to the voices so that they can use it to render the correct pitches. @@ -441,8 +439,8 @@ protected: /** This is used to control access to the rendering callback and the note trigger methods. */ CriticalSection lock; - OwnedArray voices; - ReferenceCountedArray sounds; + OwnedArray voices; + ReferenceCountedArray sounds; /** The last pitch-wheel values for each midi channel. */ int lastPitchWheelValues [16]; diff --git a/source/modules/juce_core/AppConfig.h b/source/modules/juce_core/AppConfig.h index 9731079a6..c3230e23d 100755 --- a/source/modules/juce_core/AppConfig.h +++ b/source/modules/juce_core/AppConfig.h @@ -33,11 +33,7 @@ @see jassert, jassertfalse, Logger */ -#if DEBUG - #define JUCE_LOG_ASSERTIONS 1 -#else - #define JUCE_LOG_ASSERTIONS 0 -#endif +#define JUCE_LOG_ASSERTIONS 1 //============================================================================= /** Config: JUCE_CHECK_MEMORY_LEAKS @@ -45,11 +41,7 @@ Enables a memory-leak check for certain objects when the app terminates. See the LeakedObjectDetector class and the JUCE_LEAK_DETECTOR macro for more details about enabling leak checking for specific classes. */ -//#if DEBUG #define JUCE_CHECK_MEMORY_LEAKS 1 -//#else -// #define JUCE_CHECK_MEMORY_LEAKS 0 -//#endif //============================================================================= /** Config: JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES @@ -59,7 +51,7 @@ */ #define JUCE_DONT_AUTOLINK_TO_WIN32_LIBRARIES 0 -/* Config: JUCE_INCLUDE_ZLIB_CODE +/** Config: JUCE_INCLUDE_ZLIB_CODE This can be used to disable Juce's embedded 3rd-party zlib code. You might need to tweak this if you're linking to an external zlib library in your app, but for normal apps, this option should be left alone. @@ -68,17 +60,12 @@ specify the path where your zlib headers live. */ #define JUCE_INCLUDE_ZLIB_CODE 1 -//#define JUCE_ZLIB_INCLUDE_PATH /* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS If enabled, this will add some exception-catching code to forward unhandled exceptions - to your JUCEApplication::unhandledException() callback. + to your JUCEApplicationBase::unhandledException() callback. */ -#if DEBUG - #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 -#else - #define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 -#endif +#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 0 #define JUCE_MODULE_AVAILABLE_juce_audio_basics 1 #define JUCE_MODULE_AVAILABLE_juce_audio_devices 1 diff --git a/source/modules/juce_core/containers/juce_AbstractFifo.cpp b/source/modules/juce_core/containers/juce_AbstractFifo.cpp index 666cc50d0..c14625a2e 100644 --- a/source/modules/juce_core/containers/juce_AbstractFifo.cpp +++ b/source/modules/juce_core/containers/juce_AbstractFifo.cpp @@ -141,8 +141,8 @@ public: class WriteThread : public Thread { public: - WriteThread (AbstractFifo& fifo_, int* buffer_) - : Thread ("fifo writer"), fifo (fifo_), buffer (buffer_) + WriteThread (AbstractFifo& f, int* b, Random rng) + : Thread ("fifo writer"), fifo (f), buffer (b), random (rng) { startThread(); } @@ -155,11 +155,10 @@ public: void run() { int n = 0; - Random r; while (! threadShouldExit()) { - int num = r.nextInt (2000) + 1; + int num = random.nextInt (2000) + 1; int start1, size1, start2, size2; fifo.prepareToWrite (num, start1, size1, start2, size2); @@ -181,6 +180,7 @@ public: private: AbstractFifo& fifo; int* buffer; + Random random; }; void runTest() @@ -190,10 +190,11 @@ public: int buffer [5000]; AbstractFifo fifo (numElementsInArray (buffer)); - WriteThread writer (fifo, buffer); + WriteThread writer (fifo, buffer, getRandom()); int n = 0; - Random r; + Random r = getRandom(); + r.combineSeed (12345); for (int count = 100000; --count >= 0;) { diff --git a/source/modules/juce_core/containers/juce_Array.h b/source/modules/juce_core/containers/juce_Array.h index 21d3f86c2..3e96b68d6 100644 --- a/source/modules/juce_core/containers/juce_Array.h +++ b/source/modules/juce_core/containers/juce_Array.h @@ -546,11 +546,13 @@ public: /** Adds elements from an array to the end of this array. - @param elementsToAdd the array of elements to add + @param elementsToAdd an array of some kind of object from which elements + can be constructed. @param numElementsToAdd how many elements are in this other array @see add */ - void addArray (const ElementType* elementsToAdd, int numElementsToAdd) + template + void addArray (const Type* elementsToAdd, int numElementsToAdd) { const ScopedLockType lock (getLock()); @@ -566,6 +568,22 @@ public: } } + /** Adds elements from a null-terminated array of pointers to the end of this array. + + @param elementsToAdd an array of pointers to some kind of object from which elements + can be constructed. This array must be terminated by a nullptr + @see addArray + */ + template + void addNullTerminatedArray (const Type* const* elementsToAdd) + { + int num = 0; + for (const Type* const* e = elementsToAdd; *e != nullptr; ++e) + ++num; + + addArray (elementsToAdd, num); + } + /** This swaps the contents of this array with those of another array. If you need to exchange two arrays, this is vastly quicker than using copy-by-value @@ -715,7 +733,7 @@ public: @param indexToRemove the index of the element to remove @returns the element that has been removed - @see removeValue, removeRange + @see removeFirstMatchingValue, removeAllInstancesOf, removeRange */ ElementType remove (const int indexToRemove) { @@ -782,7 +800,7 @@ public: @param startIndex the index of the first element to remove @param numberToRemove how many elements should be removed - @see remove, removeValue + @see remove, removeFirstMatchingValue, removeAllInstancesOf */ void removeRange (int startIndex, int numberToRemove) { @@ -810,7 +828,7 @@ public: /** Removes the last n elements from the array. @param howManyToRemove how many elements to remove from the end of the array - @see remove, removeValue, removeRange + @see remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ void removeLast (int howManyToRemove = 1) { @@ -829,7 +847,7 @@ public: /** Removes any elements which are also in another array. @param otherArray the other array in which to look for elements to remove - @see removeValuesNotIn, remove, removeValue, removeRange + @see removeValuesNotIn, remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ template void removeValuesIn (const OtherArrayType& otherArray) @@ -857,7 +875,7 @@ public: Only elements which occur in this other array will be retained. @param otherArray the array in which to look for elements NOT to remove - @see removeValuesIn, remove, removeValue, removeRange + @see removeValuesIn, remove, removeFirstMatchingValue, removeAllInstancesOf, removeRange */ template void removeValuesNotIn (const OtherArrayType& otherArray) diff --git a/source/modules/juce_core/containers/juce_PropertySet.cpp b/source/modules/juce_core/containers/juce_PropertySet.cpp index ba23e7ada..735dc60f0 100644 --- a/source/modules/juce_core/containers/juce_PropertySet.cpp +++ b/source/modules/juce_core/containers/juce_PropertySet.cpp @@ -65,8 +65,7 @@ void PropertySet::clear() } } -String PropertySet::getValue (const String& keyName, - const String& defaultValue) const noexcept +String PropertySet::getValue (StringRef keyName, const String& defaultValue) const noexcept { const ScopedLock sl (lock); @@ -79,8 +78,7 @@ String PropertySet::getValue (const String& keyName, : defaultValue; } -int PropertySet::getIntValue (const String& keyName, - const int defaultValue) const noexcept +int PropertySet::getIntValue (StringRef keyName, const int defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); @@ -92,8 +90,7 @@ int PropertySet::getIntValue (const String& keyName, : defaultValue; } -double PropertySet::getDoubleValue (const String& keyName, - const double defaultValue) const noexcept +double PropertySet::getDoubleValue (StringRef keyName, const double defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); @@ -105,8 +102,7 @@ double PropertySet::getDoubleValue (const String& keyName, : defaultValue; } -bool PropertySet::getBoolValue (const String& keyName, - const bool defaultValue) const noexcept +bool PropertySet::getBoolValue (StringRef keyName, const bool defaultValue) const noexcept { const ScopedLock sl (lock); const int index = properties.getAllKeys().indexOf (keyName, ignoreCaseOfKeys); @@ -118,7 +114,7 @@ bool PropertySet::getBoolValue (const String& keyName, : defaultValue; } -XmlElement* PropertySet::getXmlValue (const String& keyName) const +XmlElement* PropertySet::getXmlValue (StringRef keyName) const { return XmlDocument::parse (getValue (keyName)); } @@ -142,7 +138,7 @@ void PropertySet::setValue (const String& keyName, const var& v) } } -void PropertySet::removeValue (const String& keyName) +void PropertySet::removeValue (StringRef keyName) { if (keyName.isNotEmpty()) { @@ -163,7 +159,7 @@ void PropertySet::setValue (const String& keyName, const XmlElement* const xml) : var (xml->createDocument (String::empty, true))); } -bool PropertySet::containsKey (const String& keyName) const noexcept +bool PropertySet::containsKey (StringRef keyName) const noexcept { const ScopedLock sl (lock); return properties.getAllKeys().contains (keyName, ignoreCaseOfKeys); diff --git a/source/modules/juce_core/containers/juce_PropertySet.h b/source/modules/juce_core/containers/juce_PropertySet.h index 4ddf4d817..8d3f42950 100644 --- a/source/modules/juce_core/containers/juce_PropertySet.h +++ b/source/modules/juce_core/containers/juce_PropertySet.h @@ -69,8 +69,7 @@ public: @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ - String getValue (const String& keyName, - const String& defaultReturnValue = String::empty) const noexcept; + String getValue (StringRef keyName, const String& defaultReturnValue = String::empty) const noexcept; /** Returns one of the properties as an integer. @@ -81,8 +80,7 @@ public: @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ - int getIntValue (const String& keyName, - const int defaultReturnValue = 0) const noexcept; + int getIntValue (StringRef keyName, int defaultReturnValue = 0) const noexcept; /** Returns one of the properties as an double. @@ -93,8 +91,7 @@ public: @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ - double getDoubleValue (const String& keyName, - const double defaultReturnValue = 0.0) const noexcept; + double getDoubleValue (StringRef keyName, double defaultReturnValue = 0.0) const noexcept; /** Returns one of the properties as an boolean. @@ -108,8 +105,7 @@ public: @param keyName the name of the property to retrieve @param defaultReturnValue a value to return if the named property doesn't actually exist */ - bool getBoolValue (const String& keyName, - const bool defaultReturnValue = false) const noexcept; + bool getBoolValue (StringRef keyName, bool defaultReturnValue = false) const noexcept; /** Returns one of the properties as an XML element. @@ -122,7 +118,7 @@ public: @param keyName the name of the property to retrieve */ - XmlElement* getXmlValue (const String& keyName) const; + XmlElement* getXmlValue (StringRef keyName) const; //============================================================================== /** Sets a named property. @@ -150,10 +146,10 @@ public: /** Deletes a property. @param keyName the name of the property to delete. (This mustn't be an empty string) */ - void removeValue (const String& keyName); + void removeValue (StringRef keyName); /** Returns true if the properies include the given key. */ - bool containsKey (const String& keyName) const noexcept; + bool containsKey (StringRef keyName) const noexcept; /** Removes all values. */ void clear(); diff --git a/source/modules/juce_core/files/juce_File.cpp b/source/modules/juce_core/files/juce_File.cpp index f93f880a0..c39f0afb4 100644 --- a/source/modules/juce_core/files/juce_File.cpp +++ b/source/modules/juce_core/files/juce_File.cpp @@ -340,66 +340,67 @@ int File::hashCode() const { return fullPath.hashCode(); } int64 File::hashCode64() const { return fullPath.hashCode64(); } //============================================================================== -bool File::isAbsolutePath (const String& path) +bool File::isAbsolutePath (StringRef path) { - return path.startsWithChar (separator) + return path.text[0] == separator #if JUCE_WINDOWS - || (path.isNotEmpty() && path[1] == ':'); + || (path.isNotEmpty() && path.text[1] == ':'); #else - || path.startsWithChar ('~'); + || path.text[0] == '~'; #endif } -File File::getChildFile (String relativePath) const +File File::getChildFile (StringRef relativePath) const { if (isAbsolutePath (relativePath)) - return File (relativePath); + return File (String (relativePath.text)); + + if (relativePath.text[0] != '.') + return File (addTrailingSeparator (fullPath) + relativePath); String path (fullPath); // It's relative, so remove any ../ or ./ bits at the start.. - if (relativePath[0] == '.') + #if JUCE_WINDOWS + if (relativePath.text.indexOf ((juce_wchar) '/') >= 0) + return getChildFile (String (relativePath.text).replaceCharacter ('/', '\\')); + #endif + + while (relativePath[0] == '.') { - #if JUCE_WINDOWS - relativePath = relativePath.replaceCharacter ('/', '\\'); - #endif + const juce_wchar secondChar = relativePath.text[1]; - while (relativePath[0] == '.') + if (secondChar == '.') { - const juce_wchar secondChar = relativePath[1]; + const juce_wchar thirdChar = relativePath.text[2]; - if (secondChar == '.') - { - const juce_wchar thirdChar = relativePath[2]; - - if (thirdChar == 0 || thirdChar == separator) - { - const int lastSlash = path.lastIndexOfChar (separator); - if (lastSlash >= 0) - path = path.substring (0, lastSlash); - - relativePath = relativePath.substring (3); - } - else - { - break; - } - } - else if (secondChar == separator) + if (thirdChar == 0 || thirdChar == separator) { - relativePath = relativePath.substring (2); + const int lastSlash = path.lastIndexOfChar (separator); + if (lastSlash >= 0) + path = path.substring (0, lastSlash); + + relativePath = relativePath.text + (thirdChar == 0 ? 2 : 3); } else { break; } } + else if (secondChar == separator) + { + relativePath = relativePath.text + 2; + } + else + { + break; + } } return File (addTrailingSeparator (path) + relativePath); } -File File::getSiblingFile (const String& fileName) const +File File::getSiblingFile (StringRef fileName) const { return getParentDirectory().getChildFile (fileName); } @@ -602,23 +603,23 @@ String File::getFileExtension() const return String::empty; } -bool File::hasFileExtension (const String& possibleSuffix) const +bool File::hasFileExtension (StringRef possibleSuffix) const { if (possibleSuffix.isEmpty()) return fullPath.lastIndexOfChar ('.') <= fullPath.lastIndexOfChar (separator); - const int semicolon = possibleSuffix.indexOfChar (0, ';'); + const int semicolon = possibleSuffix.text.indexOf ((juce_wchar) ';'); if (semicolon >= 0) { - return hasFileExtension (possibleSuffix.substring (0, semicolon).trimEnd()) - || hasFileExtension (possibleSuffix.substring (semicolon + 1).trimStart()); + return hasFileExtension (String (possibleSuffix.text).substring (0, semicolon).trimEnd()) + || hasFileExtension ((possibleSuffix.text + (semicolon + 1)).findEndOfWhitespace()); } else { if (fullPath.endsWithIgnoreCase (possibleSuffix)) { - if (possibleSuffix.startsWithChar ('.')) + if (possibleSuffix.text[0] == '.') return true; const int dotPos = fullPath.length() - possibleSuffix.length() - 1; @@ -631,7 +632,7 @@ bool File::hasFileExtension (const String& possibleSuffix) const return false; } -File File::withFileExtension (const String& newExtension) const +File File::withFileExtension (StringRef newExtension) const { if (fullPath.isEmpty()) return File::nonexistent; @@ -642,7 +643,7 @@ File File::withFileExtension (const String& newExtension) const if (i >= 0) filePart = filePart.substring (0, i); - if (newExtension.isNotEmpty() && ! newExtension.startsWithChar ('.')) + if (newExtension.isNotEmpty() && newExtension.text[0] != '.') filePart << '.'; return getSiblingFile (filePart + newExtension); @@ -731,7 +732,7 @@ bool File::hasIdenticalContentTo (const File& other) const if (in1.openedOk() && in2.openedOk()) { const int bufferSize = 4096; - HeapBlock buffer1 (bufferSize), buffer2 (bufferSize); + HeapBlock buffer1 (bufferSize), buffer2 (bufferSize); for (;;) { @@ -874,7 +875,7 @@ String File::getRelativePathFrom (const File& dir) const } //============================================================================== -File File::createTempFile (const String& fileNameEnding) +File File::createTempFile (StringRef fileNameEnding) { const File tempFile (getSpecialLocation (tempDirectory) .getChildFile ("temp_" + String::toHexString (Random::getSystemRandom().nextInt())) diff --git a/source/modules/juce_core/files/juce_File.h b/source/modules/juce_core/files/juce_File.h index 6780e11d2..9ca381a69 100644 --- a/source/modules/juce_core/files/juce_File.h +++ b/source/modules/juce_core/files/juce_File.h @@ -203,7 +203,7 @@ public: @see getFileExtension, withFileExtension, getFileNameWithoutExtension */ - bool hasFileExtension (const String& extensionToTest) const; + bool hasFileExtension (StringRef extensionToTest) const; /** Returns a version of this file with a different file extension. @@ -215,7 +215,7 @@ public: @see getFileName, getFileExtension, hasFileExtension, getFileNameWithoutExtension */ - File withFileExtension (const String& newExtension) const; + File withFileExtension (StringRef newExtension) const; /** Returns the last part of the filename, without its file extension. @@ -255,7 +255,7 @@ public: @see getSiblingFile, getParentDirectory, getRelativePathFrom, isAChildOf */ - File getChildFile (String relativeOrAbsolutePath) const; + File getChildFile (StringRef relativeOrAbsolutePath) const; /** Returns a file which is in the same directory as this one. @@ -263,7 +263,7 @@ public: @see getChildFile, getParentDirectory */ - File getSiblingFile (const String& siblingFileName) const; + File getSiblingFile (StringRef siblingFileName) const; //============================================================================== /** Returns the directory that contains this file or directory. @@ -852,7 +852,7 @@ public: This will try to return the name of a non-existent temp file. To get the temp folder, you can use getSpecialLocation (File::tempDirectory). */ - static File createTempFile (const String& fileNameEnding); + static File createTempFile (StringRef fileNameEnding); //============================================================================== @@ -908,7 +908,7 @@ public: static bool areFileNamesCaseSensitive(); /** Returns true if the string seems to be a fully-specified absolute path. */ - static bool isAbsolutePath (const String& path); + static bool isAbsolutePath (StringRef path); /** Creates a file that simply contains this string, without doing the sanity-checking that the normal constructors do. diff --git a/source/modules/juce_core/json/juce_JSON.cpp b/source/modules/juce_core/json/juce_JSON.cpp index 319dba47a..59dfdade2 100644 --- a/source/modules/juce_core/json/juce_JSON.cpp +++ b/source/modules/juce_core/json/juce_JSON.cpp @@ -330,7 +330,9 @@ public: { if (v.isString()) { + out << '"'; writeString (out, v.toString().getCharPointer()); + out << '"'; } else if (v.isVoid()) { @@ -360,9 +362,6 @@ public: } } -private: - enum { indentSize = 2 }; - static void writeEscapedChar (OutputStream& out, const unsigned short value) { out << "\\u" << String::toHexString ((int) value).paddedLeft ('0', 4); @@ -370,15 +369,13 @@ private: static void writeString (OutputStream& out, String::CharPointerType t) { - out << '"'; - for (;;) { const juce_wchar c (t.getAndAdvance()); switch (c) { - case 0: out << '"'; return; + case 0: return; case '\"': out << "\\\""; break; case '\\': out << "\\\\"; break; @@ -472,8 +469,9 @@ private: if (! allOnOneLine) writeSpaces (out, indentLevel + indentSize); + out << '"'; writeString (out, v->name); - out << ": "; + out << "\": "; write (out, v->value, indentLevel + indentSize, allOnOneLine); if (v->nextListItem.get() != nullptr) @@ -494,6 +492,8 @@ private: out << '}'; } + + enum { indentSize = 2 }; }; //============================================================================== @@ -534,6 +534,14 @@ void JSON::writeToStream (OutputStream& output, const var& data, const bool allO JSONFormatter::write (output, data, 0, allOnOneLine); } +String JSON::escapeString (StringRef s) +{ + MemoryOutputStream mo; + JSONFormatter::writeString (mo, s.text); + return mo.toString(); +} + + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -616,8 +624,7 @@ public: void runTest() { beginTest ("JSON"); - Random r; - r.setSeedRandomly(); + Random r = getRandom(); expect (JSON::parse (String::empty) == var::null); expect (JSON::parse ("{}").isObject()); diff --git a/source/modules/juce_core/json/juce_JSON.h b/source/modules/juce_core/json/juce_JSON.h index ad1ced86d..150927aa9 100644 --- a/source/modules/juce_core/json/juce_JSON.h +++ b/source/modules/juce_core/json/juce_JSON.h @@ -103,6 +103,9 @@ public: const var& objectToFormat, bool allOnOneLine = false); + /** Returns a version of a string with any extended characters escaped. */ + static String escapeString (StringRef); + private: //============================================================================== JSON(); // This class can't be instantiated - just use its static methods. diff --git a/source/modules/juce_core/juce_core.h b/source/modules/juce_core/juce_core.h index 582700dac..3638878ca 100644 --- a/source/modules/juce_core/juce_core.h +++ b/source/modules/juce_core/juce_core.h @@ -119,7 +119,7 @@ /* Config: JUCE_CATCH_UNHANDLED_EXCEPTIONS If enabled, this will add some exception-catching code to forward unhandled exceptions - to your JUCEApplication::unhandledException() callback. + to your JUCEApplicationBase::unhandledException() callback. */ #ifndef JUCE_CATCH_UNHANDLED_EXCEPTIONS //#define JUCE_CATCH_UNHANDLED_EXCEPTIONS 1 @@ -145,6 +145,7 @@ namespace juce { +class StringRef; class MemoryBlock; class File; class InputStream; @@ -179,6 +180,7 @@ extern JUCE_API void JUCE_CALLTYPE logAssertion (const char* file, int line) noe #endif #include "text/juce_String.h" +#include "text/juce_StringRef.h" #include "logging/juce_Logger.h" #include "memory/juce_LeakedObjectDetector.h" #include "memory/juce_ContainerDeletePolicy.h" diff --git a/source/modules/juce_core/juce_module_info b/source/modules/juce_core/juce_module_info index 750daaf97..3667e8655 100644 --- a/source/modules/juce_core/juce_module_info +++ b/source/modules/juce_core/juce_module_info @@ -1,7 +1,7 @@ { "id": "juce_core", "name": "JUCE core classes", - "version": "2.1.2", + "version": "2.1.3", "description": "The essential set of basic JUCE classes, as required by all the other JUCE modules. Includes text, container, memory, threading and i/o functionality.", "website": "http://www.juce.com/juce", "license": "ISC Permissive", diff --git a/source/modules/juce_core/maths/juce_BigInteger.cpp b/source/modules/juce_core/maths/juce_BigInteger.cpp index 0cfc5646b..784bc5ff0 100644 --- a/source/modules/juce_core/maths/juce_BigInteger.cpp +++ b/source/modules/juce_core/maths/juce_BigInteger.cpp @@ -953,10 +953,10 @@ String BigInteger::toString (const int base, const int minimumNumCharacters) con return isNegative() ? "-" + s : s; } -void BigInteger::parseString (const String& text, const int base) +void BigInteger::parseString (StringRef text, const int base) { clear(); - String::CharPointerType t (text.getCharPointer().findEndOfWhitespace()); + String::CharPointerType t (text.text.findEndOfWhitespace()); setNegative (*t == (juce_wchar) '-'); diff --git a/source/modules/juce_core/maths/juce_BigInteger.h b/source/modules/juce_core/maths/juce_BigInteger.h index e79182034..cca7b9b07 100644 --- a/source/modules/juce_core/maths/juce_BigInteger.h +++ b/source/modules/juce_core/maths/juce_BigInteger.h @@ -282,7 +282,7 @@ public: Specify a base such as 2 (binary), 8 (octal), 10 (decimal), 16 (hex). Any invalid characters will be ignored. */ - void parseString (const String& text, int base); + void parseString (StringRef text, int base); //============================================================================== /** Turns the number into a block of binary data. diff --git a/source/modules/juce_core/maths/juce_Random.cpp b/source/modules/juce_core/maths/juce_Random.cpp index 2f7874bed..3b796730a 100644 --- a/source/modules/juce_core/maths/juce_Random.cpp +++ b/source/modules/juce_core/maths/juce_Random.cpp @@ -26,13 +26,11 @@ ============================================================================== */ -Random::Random (const int64 seedValue) noexcept - : seed (seedValue) +Random::Random (const int64 seedValue) noexcept : seed (seedValue) { } -Random::Random() - : seed (1) +Random::Random() : seed (1) { setSeedRandomly(); } @@ -163,24 +161,20 @@ public: { beginTest ("Random"); - for (int j = 10; --j >= 0;) + Random r = getRandom(); + + for (int i = 2000; --i >= 0;) { - Random r; - r.setSeedRandomly(); - - for (int i = 20; --i >= 0;) - { - expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); - expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); - expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); - expect (r.nextInt (1) == 0); - - int n = r.nextInt (50) + 1; - expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); - - n = r.nextInt (0x7ffffffe) + 1; - expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); - } + expect (r.nextDouble() >= 0.0 && r.nextDouble() < 1.0); + expect (r.nextFloat() >= 0.0f && r.nextFloat() < 1.0f); + expect (r.nextInt (5) >= 0 && r.nextInt (5) < 5); + expect (r.nextInt (1) == 0); + + int n = r.nextInt (50) + 1; + expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); + + n = r.nextInt (0x7ffffffe) + 1; + expect (r.nextInt (n) >= 0 && r.nextInt (n) < n); } } }; diff --git a/source/modules/juce_core/maths/juce_Random.h b/source/modules/juce_core/maths/juce_Random.h index 060b08472..a5a0be153 100644 --- a/source/modules/juce_core/maths/juce_Random.h +++ b/source/modules/juce_core/maths/juce_Random.h @@ -59,7 +59,6 @@ public: ~Random() noexcept; /** Returns the next random 32 bit integer. - @returns a random integer from the full range 0x80000000 to 0x7fffffff */ int nextInt() noexcept; @@ -71,29 +70,24 @@ public: int nextInt (int maxValue) noexcept; /** Returns the next 64-bit random number. - @returns a random integer from the full range 0x8000000000000000 to 0x7fffffffffffffff */ int64 nextInt64() noexcept; /** Returns the next random floating-point number. - @returns a random value in the range 0 to 1.0 */ float nextFloat() noexcept; /** Returns the next random floating-point number. - @returns a random value in the range 0 to 1.0 */ double nextDouble() noexcept; - /** Returns the next random boolean value. - */ + /** Returns the next random boolean value. */ bool nextBool() noexcept; /** Returns a BigInteger containing a random number. - @returns a random value in the range 0 to (maximumValue - 1). */ BigInteger nextLargeNumber (const BigInteger& maximumValue); @@ -108,6 +102,9 @@ public: /** Resets this Random object to a given seed value. */ void setSeed (int64 newSeed) noexcept; + /** Returns the RNG's current seed. */ + int64 getSeed() const noexcept { return seed; } + /** Merges this object's seed with another value. This sets the seed to be a value created by combining the current seed and this new value. diff --git a/source/modules/juce_core/memory/juce_MemoryBlock.cpp b/source/modules/juce_core/memory/juce_MemoryBlock.cpp index 16438f6de..f6562c2c8 100644 --- a/source/modules/juce_core/memory/juce_MemoryBlock.cpp +++ b/source/modules/juce_core/memory/juce_MemoryBlock.cpp @@ -317,11 +317,11 @@ void MemoryBlock::setBitRange (const size_t bitRangeStart, size_t numBits, int b } //============================================================================== -void MemoryBlock::loadFromHexString (const String& hex) +void MemoryBlock::loadFromHexString (StringRef hex) { ensureSize ((size_t) hex.length() >> 1); char* dest = data; - String::CharPointerType t (hex.getCharPointer()); + String::CharPointerType t (hex.text); for (;;) { @@ -373,27 +373,27 @@ String MemoryBlock::toBase64Encoding() const return destString; } -bool MemoryBlock::fromBase64Encoding (const String& s) +bool MemoryBlock::fromBase64Encoding (StringRef s) { - const int startPos = s.indexOfChar ('.') + 1; + String::CharPointerType dot (CharacterFunctions::find (s.text, CharPointer_ASCII ("."))); - if (startPos <= 0) + if (dot.isEmpty()) return false; - const int numBytesNeeded = s.substring (0, startPos - 1).getIntValue(); + const int numBytesNeeded = String (s.text, dot).getIntValue(); setSize ((size_t) numBytesNeeded, true); - const int numChars = s.length() - startPos; - - String::CharPointerType srcChars (s.getCharPointer()); - srcChars += startPos; + String::CharPointerType srcChars (dot + 1); int pos = 0; - for (int i = 0; i < numChars; ++i) + for (;;) { const char c = (char) srcChars.getAndAdvance(); + if (c == 0) + return true; + for (int j = 0; j < 64; ++j) { if (base64EncodingTable[j] == c) @@ -404,6 +404,4 @@ bool MemoryBlock::fromBase64Encoding (const String& s) } } } - - return true; } diff --git a/source/modules/juce_core/memory/juce_MemoryBlock.h b/source/modules/juce_core/memory/juce_MemoryBlock.h index 62eeec9fd..801a9e1f5 100644 --- a/source/modules/juce_core/memory/juce_MemoryBlock.h +++ b/source/modules/juce_core/memory/juce_MemoryBlock.h @@ -206,7 +206,7 @@ public: @see String::toHexString() */ - void loadFromHexString (const String& sourceHexString); + void loadFromHexString (StringRef sourceHexString); //============================================================================== /** Sets a number of bits in the memory block, treating it as a long binary sequence. */ @@ -235,7 +235,7 @@ public: @see toBase64Encoding */ - bool fromBase64Encoding (const String& encodedString); + bool fromBase64Encoding (StringRef encodedString); private: diff --git a/source/modules/juce_core/misc/juce_Uuid.cpp b/source/modules/juce_core/misc/juce_Uuid.cpp index 9df787f4a..b77c3a94e 100644 --- a/source/modules/juce_core/misc/juce_Uuid.cpp +++ b/source/modules/juce_core/misc/juce_Uuid.cpp @@ -26,32 +26,13 @@ ============================================================================== */ -namespace -{ - int64 getRandomSeedFromMACAddresses() - { - Array result; - MACAddress::findAllAddresses (result); - - Random r; - for (int i = 0; i < result.size(); ++i) - r.combineSeed (result[i].toInt64()); - - return r.nextInt64(); - } -} -//============================================================================== Uuid::Uuid() { - // The normal random seeding is pretty good, but we'll throw some MAC addresses - // into the mix too, to make it very very unlikely that two UUIDs will ever be the same.. - - static Random r1 (getRandomSeedFromMACAddresses()); - Random r2; + Random r; for (size_t i = 0; i < sizeof (uuid); ++i) - uuid[i] = (uint8) (r1.nextInt() ^ r2.nextInt()); + uuid[i] = (uint8) (r.nextInt (256)); } Uuid::~Uuid() noexcept {} @@ -70,6 +51,11 @@ Uuid& Uuid::operator= (const Uuid& other) noexcept bool Uuid::operator== (const Uuid& other) const noexcept { return memcmp (uuid, other.uuid, sizeof (uuid)) == 0; } bool Uuid::operator!= (const Uuid& other) const noexcept { return ! operator== (other); } +Uuid Uuid::null() noexcept +{ + return Uuid ((const uint8*) nullptr); +} + bool Uuid::isNull() const noexcept { for (size_t i = 0; i < sizeof (uuid); ++i) @@ -98,7 +84,7 @@ Uuid& Uuid::operator= (const String& uuidString) return *this; } -Uuid::Uuid (const uint8* const rawData) +Uuid::Uuid (const uint8* const rawData) noexcept { operator= (rawData); } diff --git a/source/modules/juce_core/misc/juce_Uuid.h b/source/modules/juce_core/misc/juce_Uuid.h index 29974dec6..06a07dd59 100644 --- a/source/modules/juce_core/misc/juce_Uuid.h +++ b/source/modules/juce_core/misc/juce_Uuid.h @@ -51,17 +51,20 @@ public: ~Uuid() noexcept; /** Creates a copy of another UUID. */ - Uuid (const Uuid& other) noexcept; + Uuid (const Uuid&) noexcept; /** Copies another UUID. */ - Uuid& operator= (const Uuid& other) noexcept; + Uuid& operator= (const Uuid&) noexcept; //============================================================================== /** Returns true if the ID is zero. */ bool isNull() const noexcept; - bool operator== (const Uuid& other) const noexcept; - bool operator!= (const Uuid& other) const noexcept; + /** Returns a null Uuid object. */ + static Uuid null() noexcept; + + bool operator== (const Uuid&) const noexcept; + bool operator!= (const Uuid&) const noexcept; //============================================================================== /** Returns a stringified version of this UUID. @@ -95,7 +98,7 @@ public: /** Creates a UUID from a 16-byte array. @see getRawData */ - Uuid (const uint8* rawData); + Uuid (const uint8* rawData) noexcept; /** Sets this UUID from 16-bytes of raw data. */ Uuid& operator= (const uint8* rawData) noexcept; diff --git a/source/modules/juce_core/native/juce_android_SystemStats.cpp b/source/modules/juce_core/native/juce_android_SystemStats.cpp index 605faac97..ae68b4753 100644 --- a/source/modules/juce_core/native/juce_android_SystemStats.cpp +++ b/source/modules/juce_core/native/juce_android_SystemStats.cpp @@ -26,8 +26,7 @@ ============================================================================== */ -JNIClassBase::JNIClassBase (const char* classPath_) - : classPath (classPath_), classRef (0) +JNIClassBase::JNIClassBase (const char* cp) : classPath (cp), classRef (0) { getClasses().add (this); } @@ -129,8 +128,7 @@ AndroidSystem::AndroidSystem() : screenWidth (0), screenHeight (0), dpi (160) { } -void AndroidSystem::initialise (JNIEnv* env, jobject activity_, - jstring appFile_, jstring appDataDir_) +void AndroidSystem::initialise (JNIEnv* env, jobject act, jstring file, jstring dataDir) { screenWidth = screenHeight = 0; dpi = 160; @@ -141,9 +139,9 @@ void AndroidSystem::initialise (JNIEnv* env, jobject activity_, systemInitialised = true; #endif - activity = GlobalRef (activity_); - appFile = juceString (env, appFile_); - appDataDir = juceString (env, appDataDir_); + activity = GlobalRef (act); + appFile = juceString (env, file); + appDataDir = juceString (env, dataDir); } void AndroidSystem::shutdown (JNIEnv* env) @@ -162,14 +160,12 @@ AndroidSystem android; //============================================================================== namespace AndroidStatsHelpers { - //============================================================================== #define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \ STATICMETHOD (getProperty, "getProperty", "(Ljava/lang/String;)Ljava/lang/String;") DECLARE_JNI_CLASS (SystemClass, "java/lang/System"); #undef JNI_CLASS_MEMBERS - //============================================================================== String getSystemProperty (const String& name) { return juceString (LocalRef ((jstring) getEnv()->CallStaticObjectMethod (SystemClass, @@ -177,7 +173,6 @@ namespace AndroidStatsHelpers javaString (name).get()))); } - //============================================================================== String getLocaleValue (bool isRegion) { return juceString (LocalRef ((jstring) getEnv()->CallStaticObjectMethod (JuceAppActivity, @@ -236,16 +231,13 @@ int SystemStats::getPageSize() //============================================================================== String SystemStats::getLogonName() { - const char* user = getenv ("USER"); + if (const char* user = getenv ("USER")) + return CharPointer_UTF8 (user); - if (user == 0) - { - struct passwd* const pw = getpwuid (getuid()); - if (pw != 0) - user = pw->pw_name; - } + if (struct passwd* const pw = getpwuid (getuid())) + return CharPointer_UTF8 (pw->pw_name); - return CharPointer_UTF8 (user); + return String::empty; } String SystemStats::getFullUserName() diff --git a/source/modules/juce_core/native/juce_linux_Files.cpp b/source/modules/juce_core/native/juce_linux_Files.cpp index 63682460f..c3d97c075 100644 --- a/source/modules/juce_core/native/juce_linux_Files.cpp +++ b/source/modules/juce_core/native/juce_linux_Files.cpp @@ -165,13 +165,13 @@ File File::getSpecialLocation (const SpecialLocationType type) { case userHomeDirectory: { - const char* homeDir = getenv ("HOME"); + if (const char* homeDir = getenv ("HOME")) + return File (CharPointer_UTF8 (homeDir)); - if (homeDir == nullptr) - if (struct passwd* const pw = getpwuid (getuid())) - homeDir = pw->pw_dir; + if (struct passwd* const pw = getpwuid (getuid())) + return File (CharPointer_UTF8 (pw->pw_dir)); - return File (CharPointer_UTF8 (homeDir)); + return File::nonexistent; } case userDocumentsDirectory: return resolveXDGFolder ("XDG_DOCUMENTS_DIR", "~"); @@ -179,7 +179,7 @@ File File::getSpecialLocation (const SpecialLocationType type) case userMoviesDirectory: return resolveXDGFolder ("XDG_VIDEOS_DIR", "~"); case userPicturesDirectory: return resolveXDGFolder ("XDG_PICTURES_DIR", "~"); case userDesktopDirectory: return resolveXDGFolder ("XDG_DESKTOP_DIR", "~/Desktop"); - case userApplicationDataDirectory: return File ("~"); + case userApplicationDataDirectory: return resolveXDGFolder ("XDG_CONFIG_HOME", "~"); case commonDocumentsDirectory: case commonApplicationDataDirectory: return File ("/var"); case globalApplicationsDirectory: return File ("/usr"); @@ -247,10 +247,9 @@ bool File::moveToTrash() const class DirectoryIterator::NativeIterator::Pimpl { public: - Pimpl (const File& directory, const String& wildCard_) + Pimpl (const File& directory, const String& wc) : parentDir (File::addTrailingSeparator (directory.getFullPathName())), - wildCard (wildCard_), - dir (opendir (directory.getFullPathName().toUTF8())) + wildCard (wc), dir (opendir (directory.getFullPathName().toUTF8())) { } diff --git a/source/modules/juce_core/native/juce_linux_SystemStats.cpp b/source/modules/juce_core/native/juce_linux_SystemStats.cpp index 0c13ff188..1062b6593 100644 --- a/source/modules/juce_core/native/juce_linux_SystemStats.cpp +++ b/source/modules/juce_core/native/juce_linux_SystemStats.cpp @@ -96,13 +96,13 @@ int SystemStats::getPageSize() //============================================================================== String SystemStats::getLogonName() { - const char* user = getenv ("USER"); + if (const char* user = getenv ("USER")) + return CharPointer_UTF8 (user); - if (user == nullptr) - if (passwd* const pw = getpwuid (getuid())) - user = pw->pw_name; + if (struct passwd* const pw = getpwuid (getuid())) + return CharPointer_UTF8 (pw->pw_name); - return CharPointer_UTF8 (user); + return String::empty; } String SystemStats::getFullUserName() diff --git a/source/modules/juce_core/native/juce_mac_Files.mm b/source/modules/juce_core/native/juce_mac_Files.mm index d2be92e35..7c2bb1838 100644 --- a/source/modules/juce_core/native/juce_mac_Files.mm +++ b/source/modules/juce_core/native/juce_mac_Files.mm @@ -205,7 +205,7 @@ File File::getSpecialLocation (const SpecialLocationType type) { File tmp ("~/Library/Caches/" + juce_getExecutableFile().getFileNameWithoutExtension()); tmp.createDirectory(); - return tmp.getFullPathName(); + return File (tmp.getFullPathName()); } #endif case userMusicDirectory: resultPath = "~/Music"; break; @@ -245,7 +245,7 @@ File File::getSpecialLocation (const SpecialLocationType type) buffer.calloc (size + 8); _NSGetExecutablePath (buffer.getData(), &size); - return String::fromUTF8 (buffer, (int) size); + return File (String::fromUTF8 (buffer, (int) size)); } default: diff --git a/source/modules/juce_core/native/juce_mac_Network.mm b/source/modules/juce_core/native/juce_mac_Network.mm index 9d38c36a7..55be8391b 100644 --- a/source/modules/juce_core/native/juce_mac_Network.mm +++ b/source/modules/juce_core/native/juce_mac_Network.mm @@ -250,18 +250,19 @@ public: private: //============================================================================== - struct DelegateClass : public ObjCClass + struct DelegateClass : public ObjCClass { - DelegateClass() : ObjCClass ("JUCEAppDelegate_") + DelegateClass() : ObjCClass ("JUCEAppDelegate_") { - addIvar ("state"); + addIvar ("state"); - addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); - addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); - addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); + addMethod (@selector (connection:didReceiveResponse:), didReceiveResponse, "v@:@@"); + addMethod (@selector (connection:didFailWithError:), didFailWithError, "v@:@@"); + addMethod (@selector (connection:didReceiveData:), didReceiveData, "v@:@@"); addMethod (@selector (connection:didSendBodyData:totalBytesWritten:totalBytesExpectedToWrite:totalBytesExpectedToWrite:), - connectionDidSendBodyData, "v@:@iii"); - addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); + connectionDidSendBodyData, "v@:@iii"); + addMethod (@selector (connectionDidFinishLoading:), connectionDidFinishLoading, "v@:@"); + addMethod (@selector (connection:willSendRequest:redirectResponse:), willSendRequest, "@@:@@"); registerClass(); } @@ -285,6 +286,11 @@ private: getState (self)->didReceiveData (newData); } + static NSURLRequest* willSendRequest (id, SEL, NSURLConnection*, NSURLRequest* request, NSURLResponse*) + { + return request; + } + static void connectionDidSendBodyData (id self, SEL, NSURLConnection*, NSInteger, NSInteger totalBytesWritten, NSInteger totalBytesExpected) { getState (self)->didSendBodyData (totalBytesWritten, totalBytesExpected); @@ -317,9 +323,8 @@ public: if (responseHeaders != nullptr && connection != nullptr && connection->headers != nil) { NSEnumerator* enumerator = [connection->headers keyEnumerator]; - NSString* key; - while ((key = [enumerator nextObject]) != nil) + while (NSString* key = [enumerator nextObject]) responseHeaders->set (nsStringToJuce (key), nsStringToJuce ((NSString*) [connection->headers objectForKey: key])); } @@ -341,7 +346,7 @@ public: JUCE_AUTORELEASEPOOL { - const int bytesRead = connection->read (static_cast (buffer), bytesToRead); + const int bytesRead = connection->read (static_cast (buffer), bytesToRead); position += bytesRead; if (bytesRead == 0) @@ -379,8 +384,7 @@ private: const bool isPost; const int timeOutMs; - void createConnection (URL::OpenStreamProgressCallback* progressCallback, - void* progressCallbackContext) + void createConnection (URL::OpenStreamProgressCallback* progressCallback, void* progressCallbackContext) { jassert (connection == nullptr); @@ -424,9 +428,9 @@ InputStream* URL::createNativeStream (const String& address, bool isPost, const OpenStreamProgressCallback* progressCallback, void* progressCallbackContext, const String& headers, const int timeOutMs, StringPairArray* responseHeaders) { - ScopedPointer wi (new WebInputStream (address, isPost, postData, - progressCallback, progressCallbackContext, - headers, timeOutMs, responseHeaders)); + ScopedPointer wi (new WebInputStream (address, isPost, postData, + progressCallback, progressCallbackContext, + headers, timeOutMs, responseHeaders)); return wi->isError() ? nullptr : wi.release(); } diff --git a/source/modules/juce_core/native/juce_posix_SharedCode.h b/source/modules/juce_core/native/juce_posix_SharedCode.h index ad77359f3..917be779f 100644 --- a/source/modules/juce_core/native/juce_posix_SharedCode.h +++ b/source/modules/juce_core/native/juce_posix_SharedCode.h @@ -34,29 +34,14 @@ CriticalSection::CriticalSection() noexcept #if ! JUCE_ANDROID pthread_mutexattr_setprotocol (&atts, PTHREAD_PRIO_INHERIT); #endif - pthread_mutex_init (&internal, &atts); -} - -CriticalSection::~CriticalSection() noexcept -{ - pthread_mutex_destroy (&internal); -} - -void CriticalSection::enter() const noexcept -{ - pthread_mutex_lock (&internal); -} - -bool CriticalSection::tryEnter() const noexcept -{ - return pthread_mutex_trylock (&internal) == 0; -} - -void CriticalSection::exit() const noexcept -{ - pthread_mutex_unlock (&internal); + pthread_mutex_init (&lock, &atts); + pthread_mutexattr_destroy (&atts); } +CriticalSection::~CriticalSection() noexcept { pthread_mutex_destroy (&lock); } +void CriticalSection::enter() const noexcept { pthread_mutex_lock (&lock); } +bool CriticalSection::tryEnter() const noexcept { return pthread_mutex_trylock (&lock) == 0; } +void CriticalSection::exit() const noexcept { pthread_mutex_unlock (&lock); } //============================================================================== WaitableEvent::WaitableEvent (const bool useManualReset) noexcept diff --git a/source/modules/juce_core/native/juce_win32_Network.cpp b/source/modules/juce_core/native/juce_win32_Network.cpp index b9803d9f3..ef31c0219 100644 --- a/source/modules/juce_core/native/juce_win32_Network.cpp +++ b/source/modules/juce_core/native/juce_win32_Network.cpp @@ -58,7 +58,7 @@ public: if (HttpQueryInfo (request, HTTP_QUERY_RAW_HEADERS_CRLF, buffer.getData(), &bufferSizeBytes, 0)) { StringArray headersArray; - headersArray.addLines (reinterpret_cast (buffer.getData())); + headersArray.addLines (String (reinterpret_cast (buffer.getData()))); for (int i = 0; i < headersArray.size(); ++i) { diff --git a/source/modules/juce_core/native/juce_win32_Registry.cpp b/source/modules/juce_core/native/juce_win32_Registry.cpp index f96ecbd3d..9c5cc3b36 100644 --- a/source/modules/juce_core/native/juce_win32_Registry.cpp +++ b/source/modules/juce_core/native/juce_win32_Registry.cpp @@ -218,7 +218,7 @@ bool WindowsRegistry::registerFileAssociation (const String& fileExtension, && setValue (key + "\\shell\\open\\command\\", targetExecutable.getFullPathName() + " \"%1\"", mode) && (iconResourceNumber == 0 || setValue (key + "\\DefaultIcon\\", - targetExecutable.getFullPathName() + "," + String (-iconResourceNumber))); + targetExecutable.getFullPathName() + "," + String (iconResourceNumber))); } // These methods are deprecated: diff --git a/source/modules/juce_core/native/juce_win32_Threads.cpp b/source/modules/juce_core/native/juce_win32_Threads.cpp index 22e5134b1..85df79937 100644 --- a/source/modules/juce_core/native/juce_win32_Threads.cpp +++ b/source/modules/juce_core/native/juce_win32_Threads.cpp @@ -28,6 +28,13 @@ HWND juce_messageWindowHandle = 0; // (this is used by other parts of the codebase) +void* getUser32Function (const char* functionName) +{ + HMODULE module = GetModuleHandleA ("user32.dll"); + jassert (module != 0); + return (void*) GetProcAddress (module, functionName); +} + //============================================================================== #if ! JUCE_USE_INTRINSICS // In newer compilers, the inline versions of these are used (in juce_Atomic.h), but in @@ -55,59 +62,33 @@ __int64 juce_InterlockedCompareExchange64 (volatile __int64* value, __int64 newV CriticalSection::CriticalSection() noexcept { // (just to check the MS haven't changed this structure and broken things...) - #if JUCE_VC7_OR_EARLIER + #if JUCE_VC7_OR_EARLIER static_jassert (sizeof (CRITICAL_SECTION) <= 24); - #else - static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (internal)); - #endif - - InitializeCriticalSection ((CRITICAL_SECTION*) internal); -} - -CriticalSection::~CriticalSection() noexcept -{ - DeleteCriticalSection ((CRITICAL_SECTION*) internal); -} + #else + static_jassert (sizeof (CRITICAL_SECTION) <= sizeof (lock)); + #endif -void CriticalSection::enter() const noexcept -{ - EnterCriticalSection ((CRITICAL_SECTION*) internal); + InitializeCriticalSection ((CRITICAL_SECTION*) lock); } -bool CriticalSection::tryEnter() const noexcept -{ - return TryEnterCriticalSection ((CRITICAL_SECTION*) internal) != FALSE; -} +CriticalSection::~CriticalSection() noexcept { DeleteCriticalSection ((CRITICAL_SECTION*) lock); } +void CriticalSection::enter() const noexcept { EnterCriticalSection ((CRITICAL_SECTION*) lock); } +bool CriticalSection::tryEnter() const noexcept { return TryEnterCriticalSection ((CRITICAL_SECTION*) lock) != FALSE; } +void CriticalSection::exit() const noexcept { LeaveCriticalSection ((CRITICAL_SECTION*) lock); } -void CriticalSection::exit() const noexcept -{ - LeaveCriticalSection ((CRITICAL_SECTION*) internal); -} //============================================================================== WaitableEvent::WaitableEvent (const bool manualReset) noexcept - : internal (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) -{ -} + : handle (CreateEvent (0, manualReset ? TRUE : FALSE, FALSE, 0)) {} -WaitableEvent::~WaitableEvent() noexcept -{ - CloseHandle (internal); -} - -bool WaitableEvent::wait (const int timeOutMillisecs) const noexcept -{ - return WaitForSingleObject (internal, (DWORD) timeOutMillisecs) == WAIT_OBJECT_0; -} +WaitableEvent::~WaitableEvent() noexcept { CloseHandle (handle); } -void WaitableEvent::signal() const noexcept -{ - SetEvent (internal); -} +void WaitableEvent::signal() const noexcept { SetEvent (handle); } +void WaitableEvent::reset() const noexcept { ResetEvent (handle); } -void WaitableEvent::reset() const noexcept +bool WaitableEvent::wait (const int timeOutMs) const noexcept { - ResetEvent (internal); + return WaitForSingleObject (handle, (DWORD) timeOutMs) == WAIT_OBJECT_0; } //============================================================================== @@ -229,17 +210,15 @@ static SleepEvent sleepEvent; void JUCE_CALLTYPE Thread::sleep (const int millisecs) { + jassert (millisecs >= 0); + if (millisecs >= 10 || sleepEvent.handle == 0) - { Sleep ((DWORD) millisecs); - } else - { // unlike Sleep() this is guaranteed to return to the current thread after // the time expires, so we'll use this for short waits, which are more likely // to need to be accurate WaitForSingleObject (sleepEvent.handle, (DWORD) millisecs); - } } void Thread::yield() @@ -250,7 +229,7 @@ void Thread::yield() //============================================================================== static int lastProcessPriority = -1; -// called by WindowDriver because Windows does weird things to process priority +// called when the app gains focus because Windows does weird things to process priority // when you swap apps, and this forces an update when the app is brought to the front. void juce_repeatLastProcessPriority() { diff --git a/source/modules/juce_core/network/juce_URL.cpp b/source/modules/juce_core/network/juce_URL.cpp index fc46865c5..46edbbfd5 100644 --- a/source/modules/juce_core/network/juce_URL.cpp +++ b/source/modules/juce_core/network/juce_URL.cpp @@ -30,8 +30,7 @@ URL::URL() { } -URL::URL (const String& url_) - : url (url_) +URL::URL (const String& u) : url (u) { int i = url.indexOfChar ('?'); @@ -198,9 +197,11 @@ namespace URLHelpers data << getMangledParameters (url) << url.getPostData(); - // just a short text attachment, so use simple url encoding.. - headers << "Content-Type: application/x-www-form-urlencoded\r\nContent-length: " - << (int) data.getDataSize() << "\r\n"; + // if the user-supplied headers didn't contain a content-type, add one now.. + if (! headers.containsIgnoreCase ("Content-Type")) + headers << "Content-Type: application/x-www-form-urlencoded\r\n"; + + headers << "Content-length: " << (int) data.getDataSize() << "\r\n"; } } @@ -313,25 +314,25 @@ bool URL::isProbablyAnEmailAddress (const String& possibleEmailAddress) return atSign > 0 && possibleEmailAddress.lastIndexOfChar ('.') > (atSign + 1) - && (! possibleEmailAddress.endsWithChar ('.')); + && ! possibleEmailAddress.endsWithChar ('.'); } //============================================================================== InputStream* URL::createInputStream (const bool usePostCommand, OpenStreamProgressCallback* const progressCallback, void* const progressCallbackContext, - const String& extraHeaders, + String headers, const int timeOutMs, StringPairArray* const responseHeaders) const { - String headers; MemoryBlock headersAndPostData; + if (! headers.endsWithChar ('\n')) + headers << "\r\n"; + if (usePostCommand) URLHelpers::createHeadersAndPostData (*this, headers, headersAndPostData); - headers += extraHeaders; - if (! headers.endsWithChar ('\n')) headers << "\r\n"; @@ -344,7 +345,7 @@ InputStream* URL::createInputStream (const bool usePostCommand, bool URL::readEntireBinaryStream (MemoryBlock& destData, const bool usePostCommand) const { - const ScopedPointer in (createInputStream (usePostCommand)); + const ScopedPointer in (createInputStream (usePostCommand)); if (in != nullptr) { @@ -357,7 +358,7 @@ bool URL::readEntireBinaryStream (MemoryBlock& destData, String URL::readEntireTextStream (const bool usePostCommand) const { - const ScopedPointer in (createInputStream (usePostCommand)); + const ScopedPointer in (createInputStream (usePostCommand)); if (in != nullptr) return in->readEntireStreamAsString(); @@ -391,10 +392,10 @@ URL URL::withFileToUpload (const String& parameterName, return u; } -URL URL::withPOSTData (const String& postData_) const +URL URL::withPOSTData (const String& newPostData) const { URL u (*this); - u.postData = postData_; + u.postData = newPostData; return u; } diff --git a/source/modules/juce_core/network/juce_URL.h b/source/modules/juce_core/network/juce_URL.h index 9381fa6ec..82ae5880a 100644 --- a/source/modules/juce_core/network/juce_URL.h +++ b/source/modules/juce_core/network/juce_URL.h @@ -251,7 +251,7 @@ public: InputStream* createInputStream (bool usePostCommand, OpenStreamProgressCallback* progressCallback = nullptr, void* progressCallbackContext = nullptr, - const String& extraHeaders = String::empty, + String extraHeaders = String::empty, int connectionTimeOutMs = 0, StringPairArray* responseHeaders = nullptr) const; diff --git a/source/modules/juce_core/streams/juce_MemoryInputStream.cpp b/source/modules/juce_core/streams/juce_MemoryInputStream.cpp index de64475c0..19b10524a 100644 --- a/source/modules/juce_core/streams/juce_MemoryInputStream.cpp +++ b/source/modules/juce_core/streams/juce_MemoryInputStream.cpp @@ -104,12 +104,12 @@ public: void runTest() { beginTest ("Basics"); - Random r; + Random r = getRandom(); int randomInt = r.nextInt(); int64 randomInt64 = r.nextInt64(); double randomDouble = r.nextDouble(); - String randomString (createRandomWideCharString()); + String randomString (createRandomWideCharString (r)); MemoryOutputStream mo; mo.writeInt (randomInt); @@ -132,10 +132,9 @@ public: expect (mi.readDoubleBigEndian() == randomDouble); } - static String createRandomWideCharString() + static String createRandomWideCharString (Random& r) { juce_wchar buffer [50] = { 0 }; - Random r; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { diff --git a/source/modules/juce_core/system/juce_PlatformDefs.h b/source/modules/juce_core/system/juce_PlatformDefs.h index 0608acbca..47b4a5b59 100644 --- a/source/modules/juce_core/system/juce_PlatformDefs.h +++ b/source/modules/juce_core/system/juce_PlatformDefs.h @@ -222,17 +222,17 @@ namespace juce #if ! JUCE_MODULE_AVAILABLE_juce_gui_basics #define JUCE_CATCH_EXCEPTION JUCE_CATCH_ALL #else - /** Used in try-catch blocks, this macro will send exceptions to the JUCEApplication + /** Used in try-catch blocks, this macro will send exceptions to the JUCEApplicationBase object so they can be logged by the application if it wants to. */ #define JUCE_CATCH_EXCEPTION \ catch (const std::exception& e) \ { \ - juce::JUCEApplication::sendUnhandledException (&e, __FILE__, __LINE__); \ + juce::JUCEApplicationBase::sendUnhandledException (&e, __FILE__, __LINE__); \ } \ catch (...) \ { \ - juce::JUCEApplication::sendUnhandledException (nullptr, __FILE__, __LINE__); \ + juce::JUCEApplicationBase::sendUnhandledException (nullptr, __FILE__, __LINE__); \ } #endif diff --git a/source/modules/juce_core/system/juce_StandardHeader.h b/source/modules/juce_core/system/juce_StandardHeader.h index 657bf7d82..3c2ab6bd3 100644 --- a/source/modules/juce_core/system/juce_StandardHeader.h +++ b/source/modules/juce_core/system/juce_StandardHeader.h @@ -36,7 +36,7 @@ */ #define JUCE_MAJOR_VERSION 2 #define JUCE_MINOR_VERSION 1 -#define JUCE_BUILDNUMBER 2 +#define JUCE_BUILDNUMBER 3 /** Current Juce version number. diff --git a/source/modules/juce_core/text/juce_CharacterFunctions.h b/source/modules/juce_core/text/juce_CharacterFunctions.h index d2cdc1208..bf206a936 100644 --- a/source/modules/juce_core/text/juce_CharacterFunctions.h +++ b/source/modules/juce_core/text/juce_CharacterFunctions.h @@ -280,6 +280,26 @@ public: return isNeg ? -v : v; } + template + struct HexParser + { + template + static ResultType parse (CharPointerType t) noexcept + { + ResultType result = 0; + + while (! t.isEmpty()) + { + const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); + + if (hexValue >= 0) + result = (result << 4) | hexValue; + } + + return result; + } + }; + //============================================================================== /** Counts the number of characters in a given string, stopping if the count exceeds a specified limit. */ diff --git a/source/modules/juce_core/text/juce_Identifier.h b/source/modules/juce_core/text/juce_Identifier.h index 74c6a74b2..0f58dbc3d 100644 --- a/source/modules/juce_core/text/juce_Identifier.h +++ b/source/modules/juce_core/text/juce_Identifier.h @@ -38,7 +38,7 @@ from a string, so it's much faster to keep a static identifier object to refer to frequently-used names, rather than constructing them each time you need it. - @see NamedPropertySet, ValueTree + @see NamedValueSet, ValueTree */ class JUCE_API Identifier { @@ -82,6 +82,9 @@ public: /** Returns this identifier's raw string pointer. */ const String::CharPointerType getCharPointer() const noexcept { return name; } + /** Returns this identifier as a StringRef. */ + operator StringRef() const noexcept { return name; } + /** Returns true if this Identifier is not null */ bool isValid() const noexcept { return name.getAddress() != nullptr; } diff --git a/source/modules/juce_core/text/juce_NewLine.h b/source/modules/juce_core/text/juce_NewLine.h index 7dcb6ab6c..980ac23cf 100644 --- a/source/modules/juce_core/text/juce_NewLine.h +++ b/source/modules/juce_core/text/juce_NewLine.h @@ -53,6 +53,11 @@ public: @see getDefault() */ operator String() const { return getDefault(); } + + /** Returns the default new-line sequence that the library uses. + @see OutputStream::setNewLineString() + */ + operator StringRef() const noexcept { return getDefault(); } }; //============================================================================== @@ -74,5 +79,8 @@ extern NewLine newLine; */ JUCE_API String& JUCE_CALLTYPE operator<< (String& string1, const NewLine&); +#if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) + inline String operator+ (String s1, const NewLine&) { return s1 += NewLine::getDefault(); } +#endif #endif // JUCE_NEWLINE_H_INCLUDED diff --git a/source/modules/juce_core/text/juce_String.cpp b/source/modules/juce_core/text/juce_String.cpp index 14c0225fd..925d81268 100644 --- a/source/modules/juce_core/text/juce_String.cpp +++ b/source/modules/juce_core/text/juce_String.cpp @@ -47,26 +47,34 @@ NewLine newLine; static inline CharPointer_wchar_t castToCharPointer_wchar_t (const void* t) noexcept { - return CharPointer_wchar_t (static_cast (t)); + return CharPointer_wchar_t (static_cast (t)); } +//============================================================================== +// (Mirrors the structure of StringHolder, but without the atomic member, so can be statically constructed) +struct EmptyString +{ + int refCount; + size_t allocatedBytes; + String::CharPointerType::CharType text; +}; + +static const EmptyString emptyString = { 0x3fffffff, sizeof (String::CharPointerType::CharType), 0 }; + //============================================================================== class StringHolder { public: - StringHolder() noexcept - : refCount (0x3fffffff), allocatedNumBytes (sizeof (*text)) - { - text[0] = 0; - } + StringHolder() JUCE_DELETED_FUNCTION; typedef String::CharPointerType CharPointerType; typedef String::CharPointerType::CharType CharType; //============================================================================== - static CharPointerType createUninitialisedBytes (const size_t numBytes) + static CharPointerType createUninitialisedBytes (size_t numBytes) { - StringHolder* const s = reinterpret_cast (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]); + numBytes = (numBytes + 3) & ~3; + StringHolder* const s = reinterpret_cast (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]); s->refCount.value = 0; s->allocatedNumBytes = numBytes; return CharPointerType (s->text); @@ -76,7 +84,7 @@ public: static CharPointerType createFromCharPointer (const CharPointer text) { if (text.getAddress() == nullptr || text.isEmpty()) - return getEmpty(); + return CharPointerType (&(emptyString.text)); CharPointer t (text); size_t bytesNeeded = sizeof (CharType); @@ -93,7 +101,7 @@ public: static CharPointerType createFromCharPointer (const CharPointer text, size_t maxChars) { if (text.getAddress() == nullptr || text.isEmpty() || maxChars == 0) - return getEmpty(); + return CharPointerType (&(emptyString.text)); CharPointer end (text); size_t numChars = 0; @@ -114,7 +122,7 @@ public: static CharPointerType createFromCharPointer (const CharPointer start, const CharPointer end) { if (start.getAddress() == nullptr || start.isEmpty()) - return getEmpty(); + return CharPointerType (&(emptyString.text)); CharPointer e (start); int numChars = 0; @@ -134,7 +142,7 @@ public: static CharPointerType createFromCharPointer (const CharPointerType start, const CharPointerType end) { if (start.getAddress() == nullptr || start.isEmpty()) - return getEmpty(); + return CharPointerType (&(emptyString.text)); const size_t numBytes = (size_t) (reinterpret_cast (end.getAddress()) - reinterpret_cast (start.getAddress())); @@ -151,21 +159,20 @@ public: return dest; } - static inline CharPointerType getEmpty() noexcept - { - return CharPointerType (empty.text); - } - //============================================================================== static void retain (const CharPointerType text) noexcept { - ++(bufferFromText (text)->refCount); + StringHolder* const b = bufferFromText (text); + + if (b != (StringHolder*) &emptyString) + ++(b->refCount); } static inline void release (StringHolder* const b) noexcept { - if (--(b->refCount) == -1 && b != &empty) - delete[] reinterpret_cast (b); + if (b != (StringHolder*) &emptyString) + if (--(b->refCount) == -1) + delete[] reinterpret_cast (b); } static void release (const CharPointerType text) noexcept @@ -174,25 +181,18 @@ public: } //============================================================================== - static CharPointerType makeUnique (const CharPointerType text) - { - StringHolder* const b = bufferFromText (text); - - if (b->refCount.get() <= 0) - return text; - - CharPointerType newText (createUninitialisedBytes (b->allocatedNumBytes)); - memcpy (newText.getAddress(), text.getAddress(), b->allocatedNumBytes); - release (b); - - return newText; - } - static CharPointerType makeUniqueWithByteSize (const CharPointerType text, size_t numBytes) { StringHolder* const b = bufferFromText (text); - if (b->refCount.get() <= 0 && b->allocatedNumBytes >= numBytes) + if (b == (StringHolder*) &emptyString) + { + CharPointerType newText (createUninitialisedBytes (numBytes)); + newText.writeNull(); + return newText; + } + + if (b->allocatedNumBytes >= numBytes && b->refCount.get() <= 0) return text; CharPointerType newText (createUninitialisedBytes (jmax (b->allocatedNumBytes, numBytes))); @@ -212,8 +212,6 @@ public: size_t allocatedNumBytes; CharType text[1]; - static StringHolder empty; - private: static inline StringHolder* bufferFromText (const CharPointerType text) noexcept { @@ -234,20 +232,15 @@ private: #else #error "native wchar_t size is unknown" #endif + + static_jassert (sizeof (EmptyString) == sizeof (StringHolder)); } }; -StringHolder StringHolder::empty; const String String::empty; //============================================================================== -void String::preallocateBytes (const size_t numBytesNeeded) -{ - text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); -} - -//============================================================================== -String::String() noexcept : text (StringHolder::getEmpty()) +String::String() noexcept : text (&(emptyString.text)) { } @@ -256,8 +249,7 @@ String::~String() noexcept StringHolder::release (text); } -String::String (const String& other) noexcept - : text (other.text) +String::String (const String& other) noexcept : text (other.text) { StringHolder::retain (text); } @@ -275,10 +267,9 @@ String& String::operator= (const String& other) noexcept } #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS -String::String (String&& other) noexcept - : text (other.text) +String::String (String&& other) noexcept : text (other.text) { - other.text = StringHolder::getEmpty(); + other.text = &(emptyString.text); } String& String::operator= (String&& other) noexcept @@ -295,6 +286,11 @@ String::String (const PreallocationBytes& preallocationSize) { } +void String::preallocateBytes (const size_t numBytesNeeded) +{ + text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); +} + //============================================================================== String::String (const char* const t) : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t))) @@ -498,12 +494,12 @@ String::String (const int number) : text (NumberToStringConverters::c String::String (const unsigned int number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const short number) : text (NumberToStringConverters::createFromInteger ((int) number)) {} String::String (const unsigned short number) : text (NumberToStringConverters::createFromInteger ((unsigned int) number)) {} -String::String (const int64 number) : text (NumberToStringConverters::createFromInteger (number)) {} +String::String (const int64 number) : text (NumberToStringConverters::createFromInteger (number)) {} String::String (const uint64 number) : text (NumberToStringConverters::createFromInteger (number)) {} -String::String (const float number) : text (NumberToStringConverters::createFromDouble ((double) number, 0)) {} +String::String (const float number) : text (NumberToStringConverters::createFromDouble ((double) number, 0)) {} String::String (const double number) : text (NumberToStringConverters::createFromDouble (number, 0)) {} -String::String (const float number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble ((double) number, numberOfDecimalPlaces)) {} +String::String (const float number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble ((double) number, numberOfDecimalPlaces)) {} String::String (const double number, const int numberOfDecimalPlaces) : text (NumberToStringConverters::createFromDouble (number, numberOfDecimalPlaces)) {} //============================================================================== @@ -512,11 +508,16 @@ int String::length() const noexcept return (int) text.length(); } -size_t String::getByteOffsetOfEnd() const noexcept +static size_t findByteOffsetOfEnd (String::CharPointerType text) noexcept { return (size_t) (((char*) text.findTerminatingNull().getAddress()) - (char*) text.getAddress()); } +size_t String::getByteOffsetOfEnd() const noexcept +{ + return findByteOffsetOfEnd (text); +} + juce_wchar String::operator[] (int index) const noexcept { jassert (index == 0 || (index > 0 && index <= (int) text.lengthUpTo ((size_t) index + 1))); @@ -550,12 +551,14 @@ JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const wchar_t* const s JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) == 0; } +JUCE_API bool JUCE_CALLTYPE operator== (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) == 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const String& s2) noexcept { return s1.compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const char* const s2) noexcept { return s1.compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const wchar_t* const s2) noexcept { return s1.compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF8 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF16 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, const CharPointer_UTF32 s2) noexcept { return s1.getCharPointer().compare (s2) != 0; } +JUCE_API bool JUCE_CALLTYPE operator!= (const String& s1, StringRef s2) noexcept { return s1.getCharPointer().compare (s2.text) != 0; } JUCE_API bool JUCE_CALLTYPE operator> (const String& s1, const String& s2) noexcept { return s1.compare (s2) > 0; } JUCE_API bool JUCE_CALLTYPE operator< (const String& s1, const String& s2) noexcept { return s1.compare (s2) < 0; } JUCE_API bool JUCE_CALLTYPE operator>= (const String& s1, const String& s2) noexcept { return s1.compare (s2) >= 0; } @@ -573,6 +576,11 @@ bool String::equalsIgnoreCase (const char* const t) const noexcept : isEmpty(); } +bool String::equalsIgnoreCase (StringRef t) const noexcept +{ + return text.compareIgnoreCase (t.text) == 0; +} + bool String::equalsIgnoreCase (const String& other) const noexcept { return text == other.text @@ -625,7 +633,7 @@ void String::appendCharPointer (const CharPointerType startOfTextToAppend, preallocateBytes (byteOffsetOfNull + (size_t) extraBytesNeeded); CharPointerType::CharType* const newStringStart = addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull); - memcpy (newStringStart, startOfTextToAppend.getAddress(), extraBytesNeeded); + memcpy (newStringStart, startOfTextToAppend.getAddress(), (size_t) extraBytesNeeded); CharPointerType (addBytesToPointer (newStringStart, extraBytesNeeded)).writeNull(); } } @@ -638,21 +646,6 @@ String& String::operator+= (const wchar_t* const t) String& String::operator+= (const char* const t) { - /* If you get an assertion here, then you're trying to create a string from 8-bit data - that contains values greater than 127. These can NOT be correctly converted to unicode - because there's no way for the String class to know what encoding was used to - create them. The source data could be UTF-8, ASCII or one of many local code-pages. - - To get around this problem, you must be more explicit when you pass an ambiguous 8-bit - string to the String class - so for example if your source data is actually UTF-8, - you'd call String (CharPointer_UTF8 ("my utf8 string..")), and it would be able to - correctly convert the multi-byte characters to unicode. It's *highly* recommended that - you use UTF-8 with escape characters in your source code to represent extended characters, - because there's no other way to represent these strings in a way that isn't dependent on - the compiler, source code editor and platform. - */ - jassert (t == nullptr || CharPointer_ASCII::isValidString (t, std::numeric_limits::max())); - appendCharPointer (CharPointer_UTF8 (t)); // (using UTF8 here triggers a faster code-path than ascii) return *this; } @@ -746,15 +739,20 @@ JUCE_API String& JUCE_CALLTYPE operator<< (String& s1, const double number) JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& text) { - const size_t numBytes = text.getNumBytesAsUTF8(); + return operator<< (stream, StringRef (text)); +} + +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef text) +{ + const size_t numBytes = CharPointer_UTF8::getBytesRequiredFor (text.text); #if (JUCE_STRING_UTF_TYPE == 8) - stream.write (text.getCharPointer().getAddress(), numBytes); + stream.write (text.text.getAddress(), numBytes); #else // (This avoids using toUTF8() to prevent the memory bloat that it would leave behind // if lots of large, persistent strings were to be written to streams). HeapBlock temp (numBytes + 1); - CharPointer_UTF8 (temp).writeAll (text.getCharPointer()); + CharPointer_UTF8 (temp).writeAll (text.text); stream.write (temp, numBytes); #endif @@ -804,7 +802,7 @@ int String::lastIndexOfChar (const juce_wchar character) const noexcept return last; } -int String::indexOfAnyOf (const String& charactersToLookFor, const int startIndex, const bool ignoreCase) const noexcept +int String::indexOfAnyOf (StringRef charactersToLookFor, const int startIndex, const bool ignoreCase) const noexcept { CharPointerType t (text); @@ -824,17 +822,17 @@ int String::indexOfAnyOf (const String& charactersToLookFor, const int startInde return -1; } -int String::indexOf (const String& other) const noexcept +int String::indexOf (StringRef other) const noexcept { return other.isEmpty() ? 0 : text.indexOf (other.text); } -int String::indexOfIgnoreCase (const String& other) const noexcept +int String::indexOfIgnoreCase (StringRef other) const noexcept { return other.isEmpty() ? 0 : CharacterFunctions::indexOfIgnoreCase (text, other.text); } -int String::indexOf (const int startIndex, const String& other) const noexcept +int String::indexOf (const int startIndex, StringRef other) const noexcept { if (other.isEmpty()) return -1; @@ -855,7 +853,7 @@ int String::indexOf (const int startIndex, const String& other) const noexcept return found; } -int String::indexOfIgnoreCase (const int startIndex, const String& other) const noexcept +int String::indexOfIgnoreCase (const int startIndex, StringRef other) const noexcept { if (other.isEmpty()) return -1; @@ -876,7 +874,7 @@ int String::indexOfIgnoreCase (const int startIndex, const String& other) const return found; } -int String::lastIndexOf (const String& other) const noexcept +int String::lastIndexOf (StringRef other) const noexcept { if (other.isNotEmpty()) { @@ -885,15 +883,12 @@ int String::lastIndexOf (const String& other) const noexcept if (i >= 0) { - CharPointerType n (text + i); - - while (i >= 0) + for (CharPointerType n (text + i); i >= 0; --i) { if (n.compareUpTo (other.text, len) == 0) return i; --n; - --i; } } } @@ -901,7 +896,7 @@ int String::lastIndexOf (const String& other) const noexcept return -1; } -int String::lastIndexOfIgnoreCase (const String& other) const noexcept +int String::lastIndexOfIgnoreCase (StringRef other) const noexcept { if (other.isNotEmpty()) { @@ -910,15 +905,12 @@ int String::lastIndexOfIgnoreCase (const String& other) const noexcept if (i >= 0) { - CharPointerType n (text + i); - - while (i >= 0) + for (CharPointerType n (text + i); i >= 0; --i) { if (n.compareIgnoreCaseUpTo (other.text, len) == 0) return i; --n; - --i; } } } @@ -926,7 +918,7 @@ int String::lastIndexOfIgnoreCase (const String& other) const noexcept return -1; } -int String::lastIndexOfAnyOf (const String& charactersToLookFor, const bool ignoreCase) const noexcept +int String::lastIndexOfAnyOf (StringRef charactersToLookFor, const bool ignoreCase) const noexcept { CharPointerType t (text); int last = -1; @@ -938,7 +930,7 @@ int String::lastIndexOfAnyOf (const String& charactersToLookFor, const bool igno return last; } -bool String::contains (const String& other) const noexcept +bool String::contains (StringRef other) const noexcept { return indexOf (other) >= 0; } @@ -948,12 +940,12 @@ bool String::containsChar (const juce_wchar character) const noexcept return text.indexOf (character) >= 0; } -bool String::containsIgnoreCase (const String& t) const noexcept +bool String::containsIgnoreCase (StringRef t) const noexcept { return indexOfIgnoreCase (t) >= 0; } -int String::indexOfWholeWord (const String& word) const noexcept +int String::indexOfWholeWord (StringRef word) const noexcept { if (word.isNotEmpty()) { @@ -975,7 +967,7 @@ int String::indexOfWholeWord (const String& word) const noexcept return -1; } -int String::indexOfWholeWordIgnoreCase (const String& word) const noexcept +int String::indexOfWholeWordIgnoreCase (StringRef word) const noexcept { if (word.isNotEmpty()) { @@ -997,12 +989,12 @@ int String::indexOfWholeWordIgnoreCase (const String& word) const noexcept return -1; } -bool String::containsWholeWord (const String& wordToLookFor) const noexcept +bool String::containsWholeWord (StringRef wordToLookFor) const noexcept { return indexOfWholeWord (wordToLookFor) >= 0; } -bool String::containsWholeWordIgnoreCase (const String& wordToLookFor) const noexcept +bool String::containsWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept { return indexOfWholeWordIgnoreCase (wordToLookFor) >= 0; } @@ -1044,18 +1036,18 @@ struct WildCardMatcher } }; -bool String::matchesWildcard (const String& wildcard, const bool ignoreCase) const noexcept +bool String::matchesWildcard (StringRef wildcard, const bool ignoreCase) const noexcept { return WildCardMatcher::matches (wildcard.text, text, ignoreCase); } //============================================================================== -String String::repeatedString (const String& stringToRepeat, int numberOfTimesToRepeat) +String String::repeatedString (StringRef stringToRepeat, int numberOfTimesToRepeat) { if (numberOfTimesToRepeat <= 0) - return empty; + return String(); - String result (PreallocationBytes (stringToRepeat.getByteOffsetOfEnd() * (size_t) numberOfTimesToRepeat)); + String result (PreallocationBytes (findByteOffsetOfEnd (stringToRepeat) * (size_t) numberOfTimesToRepeat)); CharPointerType n (result.text); while (--numberOfTimesToRepeat >= 0) @@ -1121,7 +1113,7 @@ String String::paddedRight (const juce_wchar padCharacter, int minimumLength) co } //============================================================================== -String String::replaceSection (int index, int numCharsToReplace, const String& stringToInsert) const +String String::replaceSection (int index, int numCharsToReplace, StringRef stringToInsert) const { if (index < 0) { @@ -1137,10 +1129,9 @@ String String::replaceSection (int index, int numCharsToReplace, const String& s jassertfalse; } - int i = 0; CharPointerType insertPoint (text); - while (i < index) + for (int i = 0; i < index; ++i) { if (insertPoint.isEmpty()) { @@ -1150,28 +1141,23 @@ String String::replaceSection (int index, int numCharsToReplace, const String& s } ++insertPoint; - ++i; } CharPointerType startOfRemainder (insertPoint); - i = 0; - while (i < numCharsToReplace && ! startOfRemainder.isEmpty()) - { + for (int i = 0; i < numCharsToReplace && ! startOfRemainder.isEmpty(); ++i) ++startOfRemainder; - ++i; - } if (insertPoint == text && startOfRemainder.isEmpty()) - return stringToInsert; + return stringToInsert.text; const size_t initialBytes = (size_t) (((char*) insertPoint.getAddress()) - (char*) text.getAddress()); - const size_t newStringBytes = stringToInsert.getByteOffsetOfEnd(); + const size_t newStringBytes = findByteOffsetOfEnd (stringToInsert); const size_t remainderBytes = (size_t) (((char*) startOfRemainder.findTerminatingNull().getAddress()) - (char*) startOfRemainder.getAddress()); const size_t newTotalBytes = initialBytes + newStringBytes + remainderBytes; if (newTotalBytes <= 0) - return String::empty; + return String(); String result (PreallocationBytes ((size_t) newTotalBytes)); @@ -1187,7 +1173,7 @@ String String::replaceSection (int index, int numCharsToReplace, const String& s return result; } -String String::replace (const String& stringToReplace, const String& stringToInsert, const bool ignoreCase) const +String String::replace (StringRef stringToReplace, StringRef stringToInsert, const bool ignoreCase) const { const int stringToReplaceLen = stringToReplace.length(); const int stringToInsertLen = stringToInsert.length(); @@ -1268,7 +1254,7 @@ String String::replaceCharacter (const juce_wchar charToReplace, const juce_wcha return builder.result; } -String String::replaceCharacters (const String& charactersToReplace, const String& charactersToInsertInstead) const +String String::replaceCharacters (StringRef charactersToReplace, StringRef charactersToInsertInstead) const { StringCreationHelper builder (text); @@ -1276,7 +1262,7 @@ String String::replaceCharacters (const String& charactersToReplace, const Strin { juce_wchar c = builder.source.getAndAdvance(); - const int index = charactersToReplace.indexOfChar (c); + const int index = charactersToReplace.text.indexOf (c); if (index >= 0) c = charactersToInsertInstead [index]; @@ -1290,12 +1276,12 @@ String String::replaceCharacters (const String& charactersToReplace, const Strin } //============================================================================== -bool String::startsWith (const String& other) const noexcept +bool String::startsWith (StringRef other) const noexcept { return text.compareUpTo (other.text, other.length()) == 0; } -bool String::startsWithIgnoreCase (const String& other) const noexcept +bool String::startsWithIgnoreCase (StringRef other) const noexcept { return text.compareIgnoreCaseUpTo (other.text, other.length()) == 0; } @@ -1318,7 +1304,7 @@ bool String::endsWithChar (const juce_wchar character) const noexcept return *--t == character; } -bool String::endsWith (const String& other) const noexcept +bool String::endsWith (StringRef other) const noexcept { CharPointerType end (text.findTerminatingNull()); CharPointerType otherEnd (other.text.findTerminatingNull()); @@ -1335,7 +1321,7 @@ bool String::endsWith (const String& other) const noexcept return otherEnd == other.text; } -bool String::endsWithIgnoreCase (const String& other) const noexcept +bool String::endsWithIgnoreCase (StringRef other) const noexcept { CharPointerType end (text.findTerminatingNull()); CharPointerType otherEnd (other.text.findTerminatingNull()); @@ -1360,11 +1346,12 @@ String String::toUpperCase() const for (;;) { const juce_wchar c = builder.source.toUpperCase(); - ++(builder.source); builder.write (c); if (c == 0) break; + + ++(builder.source); } return builder.result; @@ -1377,11 +1364,12 @@ String String::toLowerCase() const for (;;) { const juce_wchar c = builder.source.toLowerCase(); - ++(builder.source); builder.write (c); if (c == 0) break; + + ++(builder.source); } return builder.result; @@ -1399,7 +1387,7 @@ String String::substring (int start, const int end) const start = 0; if (end <= start) - return empty; + return String(); int i = 0; CharPointerType t1 (text); @@ -1407,7 +1395,7 @@ String String::substring (int start, const int end) const while (i < start) { if (t1.isEmpty()) - return empty; + return String(); ++i; ++t1; @@ -1441,7 +1429,7 @@ String String::substring (int start) const while (--start >= 0) { if (t.isEmpty()) - return empty; + return String(); ++t; } @@ -1459,19 +1447,19 @@ String String::getLastCharacters (const int numCharacters) const return String (text + jmax (0, length() - jmax (0, numCharacters))); } -String String::fromFirstOccurrenceOf (const String& sub, +String String::fromFirstOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { const int i = ignoreCase ? indexOfIgnoreCase (sub) : indexOf (sub); if (i < 0) - return empty; + return String(); return substring (includeSubString ? i : i + sub.length()); } -String String::fromLastOccurrenceOf (const String& sub, +String String::fromLastOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { @@ -1483,7 +1471,7 @@ String String::fromLastOccurrenceOf (const String& sub, return substring (includeSubString ? i : i + sub.length()); } -String String::upToFirstOccurrenceOf (const String& sub, +String String::upToFirstOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { @@ -1495,7 +1483,7 @@ String String::upToFirstOccurrenceOf (const String& sub, return substring (0, includeSubString ? i + sub.length() : i); } -String String::upToLastOccurrenceOf (const String& sub, +String String::upToLastOccurrenceOf (StringRef sub, const bool includeSubString, const bool ignoreCase) const { @@ -1520,7 +1508,7 @@ String String::unquoted() const const int len = length(); if (len == 0) - return empty; + return String(); const juce_wchar lastChar = text [len - 1]; const int dropAtStart = (*text == '"' || *text == '\'') ? 1 : 0; @@ -1571,7 +1559,7 @@ String String::trim() const CharPointerType trimmedEnd (findTrimmedEnd (start, end)); if (trimmedEnd <= start) - return empty; + return String(); if (text < start || trimmedEnd < end) return String (start, trimmedEnd); @@ -1607,17 +1595,17 @@ String String::trimEnd() const return *this; } -String String::trimCharactersAtStart (const String& charactersToTrim) const +String String::trimCharactersAtStart (StringRef charactersToTrim) const { CharPointerType t (text); - while (charactersToTrim.containsChar (*t)) + while (charactersToTrim.text.indexOf (*t) >= 0) ++t; return t == text ? *this : String (t); } -String String::trimCharactersAtEnd (const String& charactersToTrim) const +String String::trimCharactersAtEnd (StringRef charactersToTrim) const { if (isNotEmpty()) { @@ -1626,7 +1614,7 @@ String String::trimCharactersAtEnd (const String& charactersToTrim) const while (trimmedEnd > text) { - if (! charactersToTrim.containsChar (*--trimmedEnd)) + if (charactersToTrim.text.indexOf (*--trimmedEnd) < 0) { ++trimmedEnd; break; @@ -1641,10 +1629,10 @@ String String::trimCharactersAtEnd (const String& charactersToTrim) const } //============================================================================== -String String::retainCharacters (const String& charactersToRetain) const +String String::retainCharacters (StringRef charactersToRetain) const { if (isEmpty()) - return empty; + return String(); StringCreationHelper builder (text); @@ -1652,7 +1640,7 @@ String String::retainCharacters (const String& charactersToRetain) const { juce_wchar c = builder.source.getAndAdvance(); - if (charactersToRetain.containsChar (c)) + if (charactersToRetain.text.indexOf (c) >= 0) builder.write (c); if (c == 0) @@ -1663,10 +1651,10 @@ String String::retainCharacters (const String& charactersToRetain) const return builder.result; } -String String::removeCharacters (const String& charactersToRemove) const +String String::removeCharacters (StringRef charactersToRemove) const { if (isEmpty()) - return empty; + return String(); StringCreationHelper builder (text); @@ -1674,7 +1662,7 @@ String String::removeCharacters (const String& charactersToRemove) const { juce_wchar c = builder.source.getAndAdvance(); - if (! charactersToRemove.containsChar (c)) + if (charactersToRemove.text.indexOf (c) < 0) builder.write (c); if (c == 0) @@ -1684,53 +1672,37 @@ String String::removeCharacters (const String& charactersToRemove) const return builder.result; } -String String::initialSectionContainingOnly (const String& permittedCharacters) const +String String::initialSectionContainingOnly (StringRef permittedCharacters) const { - CharPointerType t (text); - - while (! t.isEmpty()) - { - if (! permittedCharacters.containsChar (*t)) + for (CharPointerType t (text); ! t.isEmpty(); ++t) + if (permittedCharacters.text.indexOf (*t) < 0) return String (text, t); - ++t; - } - return *this; } -String String::initialSectionNotContaining (const String& charactersToStopAt) const +String String::initialSectionNotContaining (StringRef charactersToStopAt) const { - CharPointerType t (text); - - while (! t.isEmpty()) - { - if (charactersToStopAt.containsChar (*t)) + for (CharPointerType t (text); ! t.isEmpty(); ++t) + if (charactersToStopAt.text.indexOf (*t) >= 0) return String (text, t); - ++t; - } - return *this; } -bool String::containsOnly (const String& chars) const noexcept +bool String::containsOnly (StringRef chars) const noexcept { - CharPointerType t (text); - - while (! t.isEmpty()) - if (! chars.containsChar (t.getAndAdvance())) + for (CharPointerType t (text); ! t.isEmpty();) + if (chars.text.indexOf (t.getAndAdvance()) < 0) return false; return true; } -bool String::containsAnyOf (const String& chars) const noexcept +bool String::containsAnyOf (StringRef chars) const noexcept { - CharPointerType t (text); - - while (! t.isEmpty()) - if (chars.containsChar (t.getAndAdvance())) + for (CharPointerType t (text); ! t.isEmpty();) + if (chars.text.indexOf (t.getAndAdvance()) >= 0) return true; return false; @@ -1738,16 +1710,10 @@ bool String::containsAnyOf (const String& chars) const noexcept bool String::containsNonWhitespaceChars() const noexcept { - CharPointerType t (text); - - while (! t.isEmpty()) - { + for (CharPointerType t (text); ! t.isEmpty(); ++t) if (! t.isWhitespace()) return true; - ++t; - } - return false; } @@ -1783,14 +1749,14 @@ String String::formatted (const String pf, ... ) break; // returns -1 because of an error rather than because it needs more space. } - return empty; + return String(); } //============================================================================== -int String::getIntValue() const noexcept -{ - return text.getIntValue32(); -} +int String::getIntValue() const noexcept { return text.getIntValue32(); } +int64 String::getLargeIntValue() const noexcept { return text.getIntValue64(); } +float String::getFloatValue() const noexcept { return (float) getDoubleValue(); } +double String::getDoubleValue() const noexcept { return text.getDoubleValue(); } int String::getTrailingIntValue() const noexcept { @@ -1815,78 +1781,35 @@ int String::getTrailingIntValue() const noexcept return n; } -int64 String::getLargeIntValue() const noexcept -{ - return text.getIntValue64(); -} - -float String::getFloatValue() const noexcept -{ - return (float) getDoubleValue(); -} - -double String::getDoubleValue() const noexcept -{ - return text.getDoubleValue(); -} - static const char hexDigits[] = "0123456789abcdef"; template -struct HexConverter +static String hexToString (Type v) { - static String hexToString (Type v) - { - char buffer[32]; - char* const end = buffer + 32; - char* t = end; - *--t = 0; - - do - { - *--t = hexDigits [(int) (v & 15)]; - v >>= 4; + String::CharPointerType::CharType buffer[32]; + String::CharPointerType::CharType* const end = buffer + numElementsInArray (buffer) - 1; + String::CharPointerType::CharType* t = end; + *t = 0; - } while (v != 0); - - return String (t, (size_t) (end - t) - 1); - } - - static Type stringToHex (String::CharPointerType t) noexcept + do { - Type result = 0; - - while (! t.isEmpty()) - { - const int hexValue = CharacterFunctions::getHexDigitValue (t.getAndAdvance()); - - if (hexValue >= 0) - result = (result << 4) | hexValue; - } + *--t = hexDigits [(int) (v & 15)]; + v >>= 4; - return result; - } -}; + } while (v != 0); -String String::toHexString (const int number) -{ - return HexConverter ::hexToString ((unsigned int) number); + return String (String::CharPointerType (t), + String::CharPointerType (end)); } -String String::toHexString (const int64 number) -{ - return HexConverter ::hexToString ((uint64) number); -} - -String String::toHexString (const short number) -{ - return toHexString ((int) (unsigned short) number); -} +String String::toHexString (int number) { return hexToString ((unsigned int) number); } +String String::toHexString (int64 number) { return hexToString ((uint64) number); } +String String::toHexString (short number) { return toHexString ((int) (unsigned short) number); } String String::toHexString (const void* const d, const int size, const int groupSize) { if (size <= 0) - return empty; + return String(); int numChars = (size * 2) + 2; if (groupSize > 0) @@ -1911,8 +1834,8 @@ String String::toHexString (const void* const d, const int size, const int group return s; } -int String::getHexValue32() const noexcept { return HexConverter ::stringToHex (text); } -int64 String::getHexValue64() const noexcept { return HexConverter::stringToHex (text); } +int String::getHexValue32() const noexcept { return CharacterFunctions::HexParser ::parse (text); } +int64 String::getHexValue64() const noexcept { return CharacterFunctions::HexParser::parse (text); } //============================================================================== String String::createStringFromData (const void* const unknownData, const int size) @@ -1920,7 +1843,7 @@ String String::createStringFromData (const void* const unknownData, const int si const uint8* const data = static_cast (unknownData); if (size <= 0 || data == nullptr) - return empty; + return String(); if (size == 1) return charToString ((juce_wchar) data[0]); @@ -1994,26 +1917,26 @@ struct StringEncodingConverter }; template <> -struct StringEncodingConverter +struct StringEncodingConverter { static CharPointer_UTF8 convert (const String& source) noexcept { return CharPointer_UTF8 ((CharPointer_UTF8::CharType*) source.getCharPointer().getAddress()); } }; template <> -struct StringEncodingConverter +struct StringEncodingConverter { static CharPointer_UTF16 convert (const String& source) noexcept { return CharPointer_UTF16 ((CharPointer_UTF16::CharType*) source.getCharPointer().getAddress()); } }; template <> -struct StringEncodingConverter +struct StringEncodingConverter { static CharPointer_UTF32 convert (const String& source) noexcept { return CharPointer_UTF32 ((CharPointer_UTF32::CharType*) source.getCharPointer().getAddress()); } }; -CharPointer_UTF8 String::toUTF8() const { return StringEncodingConverter ::convert (*this); } -CharPointer_UTF16 String::toUTF16() const { return StringEncodingConverter ::convert (*this); } -CharPointer_UTF32 String::toUTF32() const { return StringEncodingConverter ::convert (*this); } +CharPointer_UTF8 String::toUTF8() const { return StringEncodingConverter::convert (*this); } +CharPointer_UTF16 String::toUTF16() const { return StringEncodingConverter::convert (*this); } +CharPointer_UTF32 String::toUTF32() const { return StringEncodingConverter::convert (*this); } const char* String::toRawUTF8() const { @@ -2022,7 +1945,7 @@ const char* String::toRawUTF8() const const wchar_t* String::toWideCharPointer() const { - return StringEncodingConverter ::convert (*this).getAddress(); + return StringEncodingConverter::convert (*this).getAddress(); } std::string String::toStdString() const @@ -2047,17 +1970,17 @@ struct StringCopier size_t String::copyToUTF8 (CharPointer_UTF8::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { - return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); + return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } size_t String::copyToUTF16 (CharPointer_UTF16::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { - return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); + return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } size_t String::copyToUTF32 (CharPointer_UTF32::CharType* const buffer, size_t maxBufferSizeBytes) const noexcept { - return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); + return StringCopier::copyToBuffer (text, buffer, maxBufferSizeBytes); } //============================================================================== @@ -2075,13 +1998,57 @@ String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) CharPointer_UTF8 (buffer + bufferSizeBytes)); } - return String::empty; + return String(); } #if JUCE_MSVC #pragma warning (pop) #endif +//============================================================================== +StringRef::StringRef() noexcept : text ((const String::CharPointerType::CharType*) "\0\0\0") +{ +} + +StringRef::StringRef (const char* stringLiteral) noexcept + #if JUCE_STRING_UTF_TYPE != 8 + : text (nullptr), stringCopy (stringLiteral) + #else + : text (stringLiteral) + #endif +{ + #if JUCE_STRING_UTF_TYPE != 8 + text = stringCopy.getCharPointer(); + #endif + + jassert (stringLiteral != nullptr); // This must be a valid string literal, not a null pointer!! + + #if JUCE_NATIVE_WCHAR_IS_UTF8 + /* If you get an assertion here, then you're trying to create a string from 8-bit data + that contains values greater than 127. These can NOT be correctly converted to unicode + because there's no way for the String class to know what encoding was used to + create them. The source data could be UTF-8, ASCII or one of many local code-pages. + + To get around this problem, you must be more explicit when you pass an ambiguous 8-bit + string to the StringRef class - so for example if your source data is actually UTF-8, + you'd call StringRef (CharPointer_UTF8 ("my utf8 string..")), and it would be able to + correctly convert the multi-byte characters to unicode. It's *highly* recommended that + you use UTF-8 with escape characters in your source code to represent extended characters, + because there's no other way to represent these strings in a way that isn't dependent on + the compiler, source code editor and platform. + */ + jassert (CharPointer_ASCII::isValidString (stringLiteral, std::numeric_limits::max())); + #endif +} + +StringRef::StringRef (String::CharPointerType stringLiteral) noexcept : text (stringLiteral) +{ + jassert (stringLiteral.getAddress() != nullptr); // This must be a valid string literal, not a null pointer!! +} + +StringRef::StringRef (const String& string) noexcept : text (string.getCharPointer()) {} + + //============================================================================== //============================================================================== #if JUCE_UNIT_TESTS @@ -2094,9 +2061,9 @@ public: template struct TestUTFConversion { - static void test (UnitTest& test) + static void test (UnitTest& test, Random& r) { - String s (createRandomWideCharString()); + String s (createRandomWideCharString (r)); typename CharPointerType::CharType buffer [300]; @@ -2116,10 +2083,9 @@ public: } }; - static String createRandomWideCharString() + static String createRandomWideCharString (Random& r) { juce_wchar buffer[50] = { 0 }; - Random r; for (int i = 0; i < numElementsInArray (buffer) - 1; ++i) { @@ -2140,6 +2106,8 @@ public: void runTest() { + Random r = getRandom(); + { beginTest ("Basics"); @@ -2192,12 +2160,12 @@ public: expect (s.substring (0, 100) == s); expect (s.substring (-1, 100) == s); expect (s.substring (3) == "345678"); - expect (s.indexOf (L"45") == 4); + expect (s.indexOf (String (L"45")) == 4); expect (String ("444445").indexOf ("45") == 4); expect (String ("444445").lastIndexOfChar ('4') == 4); - expect (String ("45454545x").lastIndexOf (L"45") == 6); + expect (String ("45454545x").lastIndexOf (String (L"45")) == 6); expect (String ("45454545x").lastIndexOfAnyOf ("456") == 7); - expect (String ("45454545x").lastIndexOfAnyOf (L"456x") == 8); + expect (String ("45454545x").lastIndexOfAnyOf (String (L"456x")) == 8); expect (String ("abABaBaBa").lastIndexOfIgnoreCase ("aB") == 6); expect (s.indexOfChar (L'4') == 4); expect (s + s == "012345678012345678"); @@ -2254,21 +2222,21 @@ public: expect (s3.compareIgnoreCase (L"ABCdeFGhiJ") == 0); expect (s3.containsIgnoreCase (s3.substring (3))); expect (s3.indexOfAnyOf ("xyzf", 2, true) == 5); - expect (s3.indexOfAnyOf (L"xyzf", 2, false) == -1); + expect (s3.indexOfAnyOf (String (L"xyzf"), 2, false) == -1); expect (s3.indexOfAnyOf ("xyzF", 2, false) == 5); - expect (s3.containsAnyOf (L"zzzFs")); + expect (s3.containsAnyOf (String (L"zzzFs"))); expect (s3.startsWith ("abcd")); - expect (s3.startsWithIgnoreCase (L"abCD")); + expect (s3.startsWithIgnoreCase (String (L"abCD"))); expect (s3.startsWith (String::empty)); expect (s3.startsWithChar ('a')); expect (s3.endsWith (String ("HIJ"))); - expect (s3.endsWithIgnoreCase (L"Hij")); + expect (s3.endsWithIgnoreCase (String (L"Hij"))); expect (s3.endsWith (String::empty)); expect (s3.endsWithChar (L'J')); expect (s3.indexOf ("HIJ") == 7); - expect (s3.indexOf (L"HIJK") == -1); + expect (s3.indexOf (String (L"HIJK")) == -1); expect (s3.indexOfIgnoreCase ("hij") == 7); - expect (s3.indexOfIgnoreCase (L"hijk") == -1); + expect (s3.indexOfIgnoreCase (String (L"hijk")) == -1); expect (s3.toStdString() == s3.toRawUTF8()); String s4 (s3); @@ -2285,25 +2253,25 @@ public: String s5 ("word word2 word3"); expect (s5.containsWholeWord (String ("word2"))); expect (s5.indexOfWholeWord ("word2") == 5); - expect (s5.containsWholeWord (L"word")); + expect (s5.containsWholeWord (String (L"word"))); expect (s5.containsWholeWord ("word3")); expect (s5.containsWholeWord (s5)); - expect (s5.containsWholeWordIgnoreCase (L"Word2")); + expect (s5.containsWholeWordIgnoreCase (String (L"Word2"))); expect (s5.indexOfWholeWordIgnoreCase ("Word2") == 5); - expect (s5.containsWholeWordIgnoreCase (L"Word")); + expect (s5.containsWholeWordIgnoreCase (String (L"Word"))); expect (s5.containsWholeWordIgnoreCase ("Word3")); - expect (! s5.containsWholeWordIgnoreCase (L"Wordx")); + expect (! s5.containsWholeWordIgnoreCase (String (L"Wordx"))); expect (! s5.containsWholeWordIgnoreCase ("xWord2")); expect (s5.containsNonWhitespaceChars()); expect (s5.containsOnly ("ordw23 ")); expect (! String (" \n\r\t").containsNonWhitespaceChars()); - expect (s5.matchesWildcard (L"wor*", false)); + expect (s5.matchesWildcard (String (L"wor*"), false)); expect (s5.matchesWildcard ("wOr*", true)); - expect (s5.matchesWildcard (L"*word3", true)); + expect (s5.matchesWildcard (String (L"*word3"), true)); expect (s5.matchesWildcard ("*word?", true)); - expect (s5.matchesWildcard (L"Word*3", true)); - expect (! s5.matchesWildcard (L"*34", true)); + expect (s5.matchesWildcard (String (L"Word*3"), true)); + expect (! s5.matchesWildcard (String (L"*34"), true)); expect (String ("xx**y").matchesWildcard ("*y", true)); expect (String ("xx**y").matchesWildcard ("x*y", true)); expect (String ("xx**y").matchesWildcard ("xx*y", true)); @@ -2315,23 +2283,23 @@ public: expectEquals (s5.fromFirstOccurrenceOf (String::empty, true, false), s5); expectEquals (s5.fromFirstOccurrenceOf ("xword2", true, false), s5.substring (100)); - expectEquals (s5.fromFirstOccurrenceOf (L"word2", true, false), s5.substring (5)); + expectEquals (s5.fromFirstOccurrenceOf (String (L"word2"), true, false), s5.substring (5)); expectEquals (s5.fromFirstOccurrenceOf ("Word2", true, true), s5.substring (5)); expectEquals (s5.fromFirstOccurrenceOf ("word2", false, false), s5.getLastCharacters (6)); - expectEquals (s5.fromFirstOccurrenceOf (L"Word2", false, true), s5.getLastCharacters (6)); + expectEquals (s5.fromFirstOccurrenceOf ("Word2", false, true), s5.getLastCharacters (6)); expectEquals (s5.fromLastOccurrenceOf (String::empty, true, false), s5); - expectEquals (s5.fromLastOccurrenceOf (L"wordx", true, false), s5); + expectEquals (s5.fromLastOccurrenceOf ("wordx", true, false), s5); expectEquals (s5.fromLastOccurrenceOf ("word", true, false), s5.getLastCharacters (5)); - expectEquals (s5.fromLastOccurrenceOf (L"worD", true, true), s5.getLastCharacters (5)); + expectEquals (s5.fromLastOccurrenceOf ("worD", true, true), s5.getLastCharacters (5)); expectEquals (s5.fromLastOccurrenceOf ("word", false, false), s5.getLastCharacters (1)); - expectEquals (s5.fromLastOccurrenceOf (L"worD", false, true), s5.getLastCharacters (1)); + expectEquals (s5.fromLastOccurrenceOf ("worD", false, true), s5.getLastCharacters (1)); expect (s5.upToFirstOccurrenceOf (String::empty, true, false).isEmpty()); expectEquals (s5.upToFirstOccurrenceOf ("word4", true, false), s5); - expectEquals (s5.upToFirstOccurrenceOf (L"word2", true, false), s5.substring (0, 10)); + expectEquals (s5.upToFirstOccurrenceOf ("word2", true, false), s5.substring (0, 10)); expectEquals (s5.upToFirstOccurrenceOf ("Word2", true, true), s5.substring (0, 10)); - expectEquals (s5.upToFirstOccurrenceOf (L"word2", false, false), s5.substring (0, 5)); + expectEquals (s5.upToFirstOccurrenceOf ("word2", false, false), s5.substring (0, 5)); expectEquals (s5.upToFirstOccurrenceOf ("Word2", false, true), s5.substring (0, 5)); expectEquals (s5.upToLastOccurrenceOf (String::empty, true, false), s5); @@ -2342,15 +2310,15 @@ public: expectEquals (s5.upToLastOccurrenceOf ("word", false, false), s5.dropLastCharacters (5)); expectEquals (s5.upToLastOccurrenceOf ("Word", false, true), s5.dropLastCharacters (5)); - expectEquals (s5.replace ("word", L"xyz", false), String ("xyz xyz2 xyz3")); - expect (s5.replace (L"Word", "xyz", true) == "xyz xyz2 xyz3"); + expectEquals (s5.replace ("word", "xyz", false), String ("xyz xyz2 xyz3")); + expect (s5.replace ("Word", "xyz", true) == "xyz xyz2 xyz3"); expect (s5.dropLastCharacters (1).replace ("Word", String ("xyz"), true) == L"xyz xyz2 xyz"); expect (s5.replace ("Word", "", true) == " 2 3"); - expectEquals (s5.replace ("Word2", L"xyz", true), String ("word xyz word3")); + expectEquals (s5.replace ("Word2", "xyz", true), String ("word xyz word3")); expect (s5.replaceCharacter (L'w', 'x') != s5); expectEquals (s5.replaceCharacter ('w', L'x').replaceCharacter ('x', 'w'), s5); expect (s5.replaceCharacters ("wo", "xy") != s5); - expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", L"wo"), s5); + expectEquals (s5.replaceCharacters ("wo", "xy").replaceCharacters ("xy", "wo"), s5); expectEquals (s5.retainCharacters ("1wordxya"), String ("wordwordword")); expect (s5.retainCharacters (String::empty).isEmpty()); expect (s5.removeCharacters ("1wordxya") == " 2 3"); @@ -2380,9 +2348,9 @@ public: { beginTest ("UTF conversions"); - TestUTFConversion ::test (*this); - TestUTFConversion ::test (*this); - TestUTFConversion ::test (*this); + TestUTFConversion ::test (*this, r); + TestUTFConversion ::test (*this, r); + TestUTFConversion ::test (*this, r); } { diff --git a/source/modules/juce_core/text/juce_String.h b/source/modules/juce_core/text/juce_String.h index a25f479ec..e48a20f51 100644 --- a/source/modules/juce_core/text/juce_String.h +++ b/source/modules/juce_core/text/juce_String.h @@ -296,6 +296,9 @@ public: /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const String& other) const noexcept; + /** Case-insensitive comparison with another string. */ + bool equalsIgnoreCase (StringRef other) const noexcept; + /** Case-insensitive comparison with another string. */ bool equalsIgnoreCase (const wchar_t* other) const noexcept; @@ -340,7 +343,7 @@ public: If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ - bool startsWith (const String& text) const noexcept; + bool startsWith (StringRef text) const noexcept; /** Tests whether the string begins with a particular character. If the character is 0, this will always return false. @@ -352,13 +355,13 @@ public: If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ - bool startsWithIgnoreCase (const String& text) const noexcept; + bool startsWithIgnoreCase (StringRef text) const noexcept; /** Tests whether the string ends with another string. If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ - bool endsWith (const String& text) const noexcept; + bool endsWith (StringRef text) const noexcept; /** Tests whether the string ends with a particular character. If the character is 0, this will always return false. @@ -370,13 +373,13 @@ public: If the parameter is an empty string, this will always return true. Uses a case-insensitive comparison. */ - bool endsWithIgnoreCase (const String& text) const noexcept; + bool endsWithIgnoreCase (StringRef text) const noexcept; /** Tests whether the string contains another substring. If the parameter is an empty string, this will always return true. Uses a case-sensitive comparison. */ - bool contains (const String& text) const noexcept; + bool contains (StringRef text) const noexcept; /** Tests whether the string contains a particular character. Uses a case-sensitive comparison. @@ -386,7 +389,7 @@ public: /** Tests whether the string contains another substring. Uses a case-insensitive comparison. */ - bool containsIgnoreCase (const String& text) const noexcept; + bool containsIgnoreCase (StringRef text) const noexcept; /** Tests whether the string contains another substring as a distinct word. @@ -394,7 +397,7 @@ public: non-alphanumeric characters @see indexOfWholeWord, containsWholeWordIgnoreCase */ - bool containsWholeWord (const String& wordToLookFor) const noexcept; + bool containsWholeWord (StringRef wordToLookFor) const noexcept; /** Tests whether the string contains another substring as a distinct word. @@ -402,7 +405,7 @@ public: non-alphanumeric characters @see indexOfWholeWordIgnoreCase, containsWholeWord */ - bool containsWholeWordIgnoreCase (const String& wordToLookFor) const noexcept; + bool containsWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept; /** Finds an instance of another substring if it exists as a distinct word. @@ -411,7 +414,7 @@ public: found, then it will return -1 @see indexOfWholeWordIgnoreCase, containsWholeWord */ - int indexOfWholeWord (const String& wordToLookFor) const noexcept; + int indexOfWholeWord (StringRef wordToLookFor) const noexcept; /** Finds an instance of another substring if it exists as a distinct word. @@ -420,7 +423,7 @@ public: found, then it will return -1 @see indexOfWholeWord, containsWholeWordIgnoreCase */ - int indexOfWholeWordIgnoreCase (const String& wordToLookFor) const noexcept; + int indexOfWholeWordIgnoreCase (StringRef wordToLookFor) const noexcept; /** Looks for any of a set of characters in the string. Uses a case-sensitive comparison. @@ -428,7 +431,7 @@ public: @returns true if the string contains any of the characters from the string that is passed in. */ - bool containsAnyOf (const String& charactersItMightContain) const noexcept; + bool containsAnyOf (StringRef charactersItMightContain) const noexcept; /** Looks for a set of characters in the string. Uses a case-sensitive comparison. @@ -437,7 +440,7 @@ public: the parameter string. If this string is empty, the return value will always be true. */ - bool containsOnly (const String& charactersItMightContain) const noexcept; + bool containsOnly (StringRef charactersItMightContain) const noexcept; /** Returns true if this string contains any non-whitespace characters. @@ -455,7 +458,7 @@ public: This isn't a full-blown regex though! The only wildcard characters supported are "*" and "?". It's mainly intended for filename pattern matching. */ - bool matchesWildcard (const String& wildcard, bool ignoreCase) const noexcept; + bool matchesWildcard (StringRef wildcard, bool ignoreCase) const noexcept; //============================================================================== // Substring location methods.. @@ -488,7 +491,7 @@ public: @see indexOfChar, lastIndexOfAnyOf */ - int indexOfAnyOf (const String& charactersToLookFor, + int indexOfAnyOf (StringRef charactersToLookFor, int startIndex = 0, bool ignoreCase = false) const noexcept; @@ -497,7 +500,7 @@ public: @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return 0. */ - int indexOf (const String& textToLookFor) const noexcept; + int indexOf (StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-sensitive comparison. @@ -506,14 +509,14 @@ public: @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ - int indexOf (int startIndex, const String& textToLookFor) const noexcept; + int indexOf (int startIndex, StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-insensitive comparison. @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return 0. */ - int indexOfIgnoreCase (const String& textToLookFor) const noexcept; + int indexOfIgnoreCase (StringRef textToLookFor) const noexcept; /** Searches for a substring within this string. Uses a case-insensitive comparison. @@ -522,7 +525,7 @@ public: @returns the index of the first occurrence of this substring, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ - int indexOfIgnoreCase (int startIndex, const String& textToLookFor) const noexcept; + int indexOfIgnoreCase (int startIndex, StringRef textToLookFor) const noexcept; /** Searches for a character inside this string (working backwards from the end of the string). Uses a case-sensitive comparison. @@ -535,14 +538,14 @@ public: @returns the index of the start of the last occurrence of the substring within this string, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ - int lastIndexOf (const String& textToLookFor) const noexcept; + int lastIndexOf (StringRef textToLookFor) const noexcept; /** Searches for a substring inside this string (working backwards from the end of the string). Uses a case-insensitive comparison. @returns the index of the start of the last occurrence of the substring within this string, or -1 if it's not found. If textToLookFor is an empty string, this will always return -1. */ - int lastIndexOfIgnoreCase (const String& textToLookFor) const noexcept; + int lastIndexOfIgnoreCase (StringRef textToLookFor) const noexcept; /** Returns the index of the last character in this string that matches one of the characters passed-in to this method. @@ -556,7 +559,7 @@ public: @see lastIndexOf, indexOfAnyOf */ - int lastIndexOfAnyOf (const String& charactersToLookFor, + int lastIndexOfAnyOf (StringRef charactersToLookFor, bool ignoreCase = false) const noexcept; @@ -640,7 +643,7 @@ public: @see upToFirstOccurrenceOf, fromLastOccurrenceOf */ - String fromFirstOccurrenceOf (const String& substringToStartFrom, + String fromFirstOccurrenceOf (StringRef substringToStartFrom, bool includeSubStringInResult, bool ignoreCase) const; @@ -652,7 +655,7 @@ public: @see fromFirstOccurrenceOf, upToLastOccurrenceOf */ - String fromLastOccurrenceOf (const String& substringToFind, + String fromLastOccurrenceOf (StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const; @@ -669,7 +672,7 @@ public: @see upToLastOccurrenceOf, fromFirstOccurrenceOf */ - String upToFirstOccurrenceOf (const String& substringToEndWith, + String upToFirstOccurrenceOf (StringRef substringToEndWith, bool includeSubStringInResult, bool ignoreCase) const; @@ -680,7 +683,7 @@ public: @see upToFirstOccurrenceOf, fromFirstOccurrenceOf */ - String upToLastOccurrenceOf (const String& substringToFind, + String upToLastOccurrenceOf (StringRef substringToFind, bool includeSubStringInResult, bool ignoreCase) const; @@ -700,7 +703,7 @@ public: @param charactersToTrim the set of characters to remove. @see trim, trimStart, trimCharactersAtEnd */ - String trimCharactersAtStart (const String& charactersToTrim) const; + String trimCharactersAtStart (StringRef charactersToTrim) const; /** Returns a copy of this string, having removed a specified set of characters from its end. Characters are removed from the end of the string until it finds one that is not in the @@ -708,7 +711,7 @@ public: @param charactersToTrim the set of characters to remove. @see trim, trimEnd, trimCharactersAtStart */ - String trimCharactersAtEnd (const String& charactersToTrim) const; + String trimCharactersAtEnd (StringRef charactersToTrim) const; //============================================================================== /** Returns an upper-case version of this string. */ @@ -735,7 +738,7 @@ public: */ String replaceSection (int startIndex, int numCharactersToReplace, - const String& stringToInsert) const; + StringRef stringToInsert) const; /** Replaces all occurrences of a substring with another string. @@ -744,8 +747,8 @@ public: Note that this is a const method, and won't alter the string itself. */ - String replace (const String& stringToReplace, - const String& stringToInsertInstead, + String replace (StringRef stringToReplace, + StringRef stringToInsertInstead, bool ignoreCase = false) const; /** Returns a string with all occurrences of a character replaced with a different one. */ @@ -762,8 +765,8 @@ public: Note that this is a const method, and won't affect the string itself. */ - String replaceCharacters (const String& charactersToReplace, - const String& charactersToInsertInstead) const; + String replaceCharacters (StringRef charactersToReplace, + StringRef charactersToInsertInstead) const; /** Returns a version of this string that only retains a fixed set of characters. @@ -774,7 +777,7 @@ public: Note that this is a const method, and won't alter the string itself. */ - String retainCharacters (const String& charactersToRetain) const; + String retainCharacters (StringRef charactersToRetain) const; /** Returns a version of this string with a set of characters removed. @@ -785,14 +788,14 @@ public: Note that this is a const method, and won't alter the string itself. */ - String removeCharacters (const String& charactersToRemove) const; + String removeCharacters (StringRef charactersToRemove) const; /** Returns a section from the start of the string that only contains a certain set of characters. This returns the leftmost section of the string, up to (and not including) the first character that doesn't appear in the string passed in. */ - String initialSectionContainingOnly (const String& permittedCharacters) const; + String initialSectionContainingOnly (StringRef permittedCharacters) const; /** Returns a section from the start of the string that only contains a certain set of characters. @@ -800,7 +803,7 @@ public: first character that occurs in the string passed in. (If none of the specified characters are found in the string, the return value will just be the original string). */ - String initialSectionNotContaining (const String& charactersToStopAt) const; + String initialSectionNotContaining (StringRef charactersToStopAt) const; //============================================================================== /** Checks whether the string might be in quotation marks. @@ -843,7 +846,7 @@ public: @param stringToRepeat the string to repeat @param numberOfTimesToRepeat how many times to repeat it */ - static String repeatedString (const String& stringToRepeat, + static String repeatedString (StringRef stringToRepeat, int numberOfTimesToRepeat); /** Returns a copy of this string with the specified character repeatedly added to its @@ -950,7 +953,6 @@ public: int getIntValue() const noexcept; /** Reads the value of the string as a decimal number (up to 64 bits in size). - @returns the value of the string as a 64 bit signed base-10 integer. */ int64 getLargeIntValue() const noexcept; @@ -1293,6 +1295,7 @@ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF16 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, const CharPointer_UTF32 string2) noexcept; + /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ @@ -1305,6 +1308,7 @@ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF16 string2) noexcept; /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, const CharPointer_UTF32 string2) noexcept; + /** Case-sensitive comparison of two strings. */ JUCE_API bool JUCE_CALLTYPE operator> (const String& string1, const String& string2) noexcept; /** Case-sensitive comparison of two strings. */ @@ -1336,5 +1340,8 @@ std::basic_ostream & JUCE_CALLTYPE operator<< (std::basic_ostre /** Writes a string to an OutputStream as UTF8. */ JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, const String& stringToWrite); +/** Writes a string to an OutputStream as UTF8. */ +JUCE_API OutputStream& JUCE_CALLTYPE operator<< (OutputStream& stream, StringRef stringToWrite); + #endif // JUCE_STRING_H_INCLUDED diff --git a/source/modules/juce_core/text/juce_StringArray.cpp b/source/modules/juce_core/text/juce_StringArray.cpp index d02da0fab..035381d2c 100644 --- a/source/modules/juce_core/text/juce_StringArray.cpp +++ b/source/modules/juce_core/text/juce_StringArray.cpp @@ -47,47 +47,29 @@ StringArray::StringArray (const String& firstValue) strings.add (firstValue); } -namespace StringArrayHelpers -{ - template - void addArray (Array& dest, const CharType* const* strings) - { - if (strings != nullptr) - while (*strings != nullptr) - dest.add (*strings++); - } - - template - void addArray (Array& dest, const Type* const strings, const int numberOfStrings) - { - for (int i = 0; i < numberOfStrings; ++i) - dest.add (strings [i]); - } -} - StringArray::StringArray (const String* initialStrings, int numberOfStrings) { - StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings); + strings.addArray (initialStrings, numberOfStrings); } -StringArray::StringArray (const char* const* const initialStrings) +StringArray::StringArray (const char* const* initialStrings) { - StringArrayHelpers::addArray (strings, initialStrings); + strings.addNullTerminatedArray (initialStrings); } -StringArray::StringArray (const char* const* const initialStrings, const int numberOfStrings) +StringArray::StringArray (const char* const* initialStrings, int numberOfStrings) { - StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings); + strings.addArray (initialStrings, numberOfStrings); } -StringArray::StringArray (const wchar_t* const* const initialStrings) +StringArray::StringArray (const wchar_t* const* initialStrings) { - StringArrayHelpers::addArray (strings, initialStrings); + strings.addNullTerminatedArray (initialStrings); } -StringArray::StringArray (const wchar_t* const* const initialStrings, const int numberOfStrings) +StringArray::StringArray (const wchar_t* const* initialStrings, int numberOfStrings) { - StringArrayHelpers::addArray (strings, initialStrings, numberOfStrings); + strings.addArray (initialStrings, numberOfStrings); } StringArray& StringArray::operator= (const StringArray& other) @@ -110,14 +92,7 @@ StringArray::~StringArray() bool StringArray::operator== (const StringArray& other) const noexcept { - if (other.size() != size()) - return false; - - for (int i = size(); --i >= 0;) - if (other.strings.getReference(i) != strings.getReference(i)) - return false; - - return true; + return strings == other.strings; } bool StringArray::operator!= (const StringArray& other) const noexcept @@ -150,7 +125,6 @@ const String& StringArray::operator[] (const int index) const noexcept String& StringArray::getReference (const int index) noexcept { - jassert (isPositiveAndBelow (index, strings.size())); return strings.getReference (index); } @@ -190,25 +164,12 @@ void StringArray::set (const int index, const String& newString) strings.set (index, newString); } -bool StringArray::contains (const String& stringToLookFor, const bool ignoreCase) const +bool StringArray::contains (StringRef stringToLookFor, const bool ignoreCase) const { - if (ignoreCase) - { - for (int i = size(); --i >= 0;) - if (strings.getReference(i).equalsIgnoreCase (stringToLookFor)) - return true; - } - else - { - for (int i = size(); --i >= 0;) - if (stringToLookFor == strings.getReference(i)) - return true; - } - - return false; + return indexOf (stringToLookFor, ignoreCase) >= 0; } -int StringArray::indexOf (const String& stringToLookFor, const bool ignoreCase, int i) const +int StringArray::indexOf (StringRef stringToLookFor, const bool ignoreCase, int i) const { if (i < 0) i = 0; @@ -217,23 +178,15 @@ int StringArray::indexOf (const String& stringToLookFor, const bool ignoreCase, if (ignoreCase) { - while (i < numElements) - { + for (; i < numElements; ++i) if (strings.getReference(i).equalsIgnoreCase (stringToLookFor)) return i; - - ++i; - } } else { - while (i < numElements) - { + for (; i < numElements; ++i) if (stringToLookFor == strings.getReference (i)) return i; - - ++i; - } } return -1; @@ -245,8 +198,7 @@ void StringArray::remove (const int index) strings.remove (index); } -void StringArray::removeString (const String& stringToRemove, - const bool ignoreCase) +void StringArray::removeString (StringRef stringToRemove, const bool ignoreCase) { if (ignoreCase) { @@ -325,7 +277,7 @@ void StringArray::move (const int currentIndex, int newIndex) noexcept //============================================================================== -String StringArray::joinIntoString (const String& separator, int start, int numberToJoin) const +String StringArray::joinIntoString (StringRef separator, int start, int numberToJoin) const { const int last = (numberToJoin < 0) ? size() : jmin (size(), start + numberToJoin); @@ -339,7 +291,7 @@ String StringArray::joinIntoString (const String& separator, int start, int numb if (start == last - 1) return strings.getReference (start); - const size_t separatorBytes = separator.getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); + const size_t separatorBytes = separator.text.sizeInBytes() - sizeof (String::CharPointerType::CharType); size_t bytesNeeded = separatorBytes * (size_t) (last - start - 1); for (int i = start; i < last; ++i) @@ -358,7 +310,7 @@ String StringArray::joinIntoString (const String& separator, int start, int numb dest.writeAll (s.getCharPointer()); if (++start < last && separatorBytes > 0) - dest.writeAll (separator.getCharPointer()); + dest.writeAll (separator.text); } dest.writeNull(); @@ -366,23 +318,22 @@ String StringArray::joinIntoString (const String& separator, int start, int numb return result; } -int StringArray::addTokens (const String& text, const bool preserveQuotedStrings) +int StringArray::addTokens (StringRef text, const bool preserveQuotedStrings) { return addTokens (text, " \n\r\t", preserveQuotedStrings ? "\"" : ""); } -int StringArray::addTokens (const String& text, const String& breakCharacters, const String& quoteCharacters) +int StringArray::addTokens (StringRef text, StringRef breakCharacters, StringRef quoteCharacters) { int num = 0; - String::CharPointerType t (text.getCharPointer()); - if (! t.isEmpty()) + if (text.isNotEmpty()) { - for (;;) + for (String::CharPointerType t (text.text);;) { String::CharPointerType tokenEnd (CharacterFunctions::findEndOfToken (t, - breakCharacters.getCharPointer(), - quoteCharacters.getCharPointer())); + breakCharacters.text, + quoteCharacters.text)); strings.add (String (t, tokenEnd)); ++num; @@ -396,10 +347,10 @@ int StringArray::addTokens (const String& text, const String& breakCharacters, c return num; } -int StringArray::addLines (const String& sourceText) +int StringArray::addLines (StringRef sourceText) { int numLines = 0; - String::CharPointerType text (sourceText.getCharPointer()); + String::CharPointerType text (sourceText.text); bool finished = text.isEmpty(); while (! finished) @@ -425,24 +376,23 @@ int StringArray::addLines (const String& sourceText) return numLines; } -StringArray StringArray::fromTokens (const String& stringToTokenise, - bool preserveQuotedStrings) +StringArray StringArray::fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings) { StringArray s; s.addTokens (stringToTokenise, preserveQuotedStrings); return s; } -StringArray StringArray::fromTokens (const String& stringToTokenise, - const String& breakCharacters, - const String& quoteCharacters) +StringArray StringArray::fromTokens (StringRef stringToTokenise, + StringRef breakCharacters, + StringRef quoteCharacters) { StringArray s; s.addTokens (stringToTokenise, breakCharacters, quoteCharacters); return s; } -StringArray StringArray::fromLines (const String& stringToBreakUp) +StringArray StringArray::fromLines (StringRef stringToBreakUp) { StringArray s; s.addLines (stringToBreakUp); @@ -456,9 +406,7 @@ void StringArray::removeDuplicates (const bool ignoreCase) { const String s (strings.getReference(i)); - int nextIndex = i + 1; - - for (;;) + for (int nextIndex = i + 1;;) { nextIndex = indexOf (s, ignoreCase, nextIndex); diff --git a/source/modules/juce_core/text/juce_StringArray.h b/source/modules/juce_core/text/juce_StringArray.h index e3c0de66c..b49296387 100644 --- a/source/modules/juce_core/text/juce_StringArray.h +++ b/source/modules/juce_core/text/juce_StringArray.h @@ -44,10 +44,10 @@ public: StringArray() noexcept; /** Creates a copy of another string array */ - StringArray (const StringArray& other); + StringArray (const StringArray&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - StringArray (StringArray&& other) noexcept; + StringArray (StringArray&&) noexcept; #endif /** Creates an array containing a single string. */ @@ -90,27 +90,27 @@ public: ~StringArray(); /** Copies the contents of another string array into this one */ - StringArray& operator= (const StringArray& other); + StringArray& operator= (const StringArray&); #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS - StringArray& operator= (StringArray&& other) noexcept; + StringArray& operator= (StringArray&&) noexcept; #endif /** Swaps the contents of this and another StringArray. */ - void swapWith (StringArray& other) noexcept; + void swapWith (StringArray&) noexcept; //============================================================================== /** Compares two arrays. Comparisons are case-sensitive. @returns true only if the other array contains exactly the same strings in the same order */ - bool operator== (const StringArray& other) const noexcept; + bool operator== (const StringArray&) const noexcept; /** Compares two arrays. Comparisons are case-sensitive. @returns false if the other array contains exactly the same strings in the same order */ - bool operator!= (const StringArray& other) const noexcept; + bool operator!= (const StringArray&) const noexcept; //============================================================================== /** Returns the number of strings in the array */ @@ -153,7 +153,7 @@ public: @returns true if the string is found inside the array */ - bool contains (const String& stringToLookFor, + bool contains (StringRef stringToLookFor, bool ignoreCase = false) const; /** Searches for a string in the array. @@ -166,7 +166,7 @@ public: @returns the index of the first occurrence of the string in this array, or -1 if it isn't found. */ - int indexOf (const String& stringToLookFor, + int indexOf (StringRef stringToLookFor, bool ignoreCase = false, int startIndex = 0) const; @@ -214,8 +214,7 @@ public: @returns the number of tokens added @see fromTokens */ - int addTokens (const String& stringToTokenise, - bool preserveQuotedStrings); + int addTokens (StringRef stringToTokenise, bool preserveQuotedStrings); /** Breaks up a string into tokens and adds them to this array. @@ -231,9 +230,9 @@ public: @returns the number of tokens added @see fromTokens */ - int addTokens (const String& stringToTokenise, - const String& breakCharacters, - const String& quoteCharacters); + int addTokens (StringRef stringToTokenise, + StringRef breakCharacters, + StringRef quoteCharacters); /** Breaks up a string into lines and adds them to this array. @@ -241,7 +240,7 @@ public: to the array. Line-break characters are omitted from the strings that are added to the array. */ - int addLines (const String& stringToBreakUp); + int addLines (StringRef stringToBreakUp); /** Returns an array containing the tokens in a given string. @@ -249,7 +248,7 @@ public: token delimiters, and return these tokens as an array. @see addTokens */ - static StringArray fromTokens (const String& stringToTokenise, + static StringArray fromTokens (StringRef stringToTokenise, bool preserveQuotedStrings); /** Returns an array containing the tokens in a given string. @@ -265,9 +264,9 @@ public: between quotes is not broken up into tokens. @see addTokens */ - static StringArray fromTokens (const String& stringToTokenise, - const String& breakCharacters, - const String& quoteCharacters); + static StringArray fromTokens (StringRef stringToTokenise, + StringRef breakCharacters, + StringRef quoteCharacters); /** Returns an array containing the lines in a given string. @@ -275,7 +274,7 @@ public: array containing these lines. Line-break characters are omitted from the strings that are added to the array. */ - static StringArray fromLines (const String& stringToBreakUp); + static StringArray fromLines (StringRef stringToBreakUp); //============================================================================== /** Removes all elements from the array. */ @@ -295,7 +294,7 @@ public: This will remove the first occurrence of the given string from the array. The comparison may be case-insensitive depending on the ignoreCase parameter. */ - void removeString (const String& stringToRemove, + void removeString (StringRef stringToRemove, bool ignoreCase = false); /** Removes a range of elements from the array. @@ -379,7 +378,7 @@ public: @param numberOfElements how many elements to join together. If this is less than zero, all available elements will be used. */ - String joinIntoString (const String& separatorString, + String joinIntoString (StringRef separatorString, int startIndex = 0, int numberOfElements = -1) const; @@ -410,7 +409,7 @@ public: private: //============================================================================== - Array strings; + Array strings; JUCE_LEAK_DETECTOR (StringArray) }; diff --git a/source/modules/juce_core/text/juce_StringPairArray.cpp b/source/modules/juce_core/text/juce_StringPairArray.cpp index f9cb60fe6..7912cd976 100644 --- a/source/modules/juce_core/text/juce_StringPairArray.cpp +++ b/source/modules/juce_core/text/juce_StringPairArray.cpp @@ -63,12 +63,12 @@ bool StringPairArray::operator!= (const StringPairArray& other) const return ! operator== (other); } -const String& StringPairArray::operator[] (const String& key) const +const String& StringPairArray::operator[] (StringRef key) const { return values [keys.indexOf (key, ignoreCase)]; } -String StringPairArray::getValue (const String& key, const String& defaultReturnValue) const +String StringPairArray::getValue (StringRef key, const String& defaultReturnValue) const { const int i = keys.indexOf (key, ignoreCase); @@ -105,7 +105,7 @@ void StringPairArray::clear() values.clear(); } -void StringPairArray::remove (const String& key) +void StringPairArray::remove (StringRef key) { remove (keys.indexOf (key, ignoreCase)); } diff --git a/source/modules/juce_core/text/juce_StringPairArray.h b/source/modules/juce_core/text/juce_StringPairArray.h index f9c1bb1db..add4a60c0 100644 --- a/source/modules/juce_core/text/juce_StringPairArray.h +++ b/source/modules/juce_core/text/juce_StringPairArray.h @@ -77,15 +77,13 @@ public: @see getValue */ - const String& operator[] (const String& key) const; + const String& operator[] (StringRef key) const; /** Finds the value corresponding to a key string. - If no such key is found, this will just return the value provided as a default. - @see operator[] */ - String getValue (const String& key, const String& defaultReturnValue) const; + String getValue (StringRef, const String& defaultReturnValue) const; /** Returns a list of all keys in the array. */ @@ -100,14 +98,12 @@ public: //============================================================================== /** Adds or amends a key/value pair. - If a value already exists with this key, its value will be overwritten, otherwise the key/value pair will be added to the array. */ void set (const String& key, const String& value); /** Adds the items from another array to this one. - This is equivalent to using set() to add each of the pairs from the other array. */ void addArray (const StringPairArray& other); @@ -117,13 +113,11 @@ public: void clear(); /** Removes a string from the array based on its key. - If the key isn't found, nothing will happen. */ - void remove (const String& key); + void remove (StringRef key); /** Removes a string from the array based on its index. - If the index is out-of-range, no action will be taken. */ void remove (int index); diff --git a/source/modules/juce_core/text/juce_StringPool.cpp b/source/modules/juce_core/text/juce_StringPool.cpp index 672b3025a..88ea9a0ff 100644 --- a/source/modules/juce_core/text/juce_StringPool.cpp +++ b/source/modules/juce_core/text/juce_StringPool.cpp @@ -48,33 +48,32 @@ namespace StringPoolHelpers strings.insert (start, newString); return strings.getReference (start).getCharPointer(); } - else - { - const String& startString = strings.getReference (start); - - if (startString == newString) - return startString.getCharPointer(); - const int halfway = (start + end) >> 1; + const String& startString = strings.getReference (start); - if (halfway == start) - { - if (startString.compare (newString) < 0) - ++start; + if (startString == newString) + return startString.getCharPointer(); - strings.insert (start, newString); - return strings.getReference (start).getCharPointer(); - } + const int halfway = (start + end) >> 1; - const int comp = strings.getReference (halfway).compare (newString); + if (halfway == start) + { + if (startString.compare (newString) < 0) + ++start; - if (comp == 0) - return strings.getReference (halfway).getCharPointer(); - else if (comp < 0) - start = halfway; - else - end = halfway; + strings.insert (start, newString); + return strings.getReference (start).getCharPointer(); } + + const int comp = strings.getReference (halfway).compare (newString); + + if (comp == 0) + return strings.getReference (halfway).getCharPointer(); + + if (comp < 0) + start = halfway; + else + end = halfway; } } } diff --git a/source/modules/juce_core/text/juce_StringRef.h b/source/modules/juce_core/text/juce_StringRef.h new file mode 100644 index 000000000..40e926880 --- /dev/null +++ b/source/modules/juce_core/text/juce_StringRef.h @@ -0,0 +1,135 @@ +/* + ============================================================================== + + This file is part of the juce_core module of the JUCE library. + Copyright (c) 2013 - Raw Material Software Ltd. + + 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. + + 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. + + ------------------------------------------------------------------------------ + + NOTE! This permissive ISC license applies ONLY to files within the juce_core module! + All other JUCE modules are covered by a dual GPL/commercial license, so if you are + using any other modules, be sure to check that you also comply with their license. + + For more details, visit www.juce.com + + ============================================================================== +*/ + +#ifndef JUCE_STRINGREF_H_INCLUDED +#define JUCE_STRINGREF_H_INCLUDED + +//============================================================================== +/** + A simple class for holding temporary references to a string literal or String. + + Unlike a real String object, the StringRef does not allocate any memory or + take ownership of the strings you give to it - it simply holds a reference to + a string that has been allocated elsewhere. + The main purpose of the class is to be used instead of a const String& as the type + of function arguments where the caller may pass either a string literal or a String + object. This means that when the called uses a string literal, there's no need + for an temporary String object to be allocated, and this cuts down overheads + substantially. + + Because the class is simply a wrapper around a pointer, you should always pass + it by value, not by reference. + + @code + void myStringFunction1 (const String&); + void myStringFunction2 (StringRef); + + myStringFunction1 ("abc"); // Implicitly allocates a temporary String object. + myStringFunction2 ("abc"); // Much faster, as no local allocations are needed. + @endcode + + For examples of it in use, see the XmlElement or StringArray classes. + + Bear in mind that there are still many cases where it's better to use an argument + which is a const String&. For example if the function stores the string or needs + to internally create a String from the argument, then it's better for the original + argument to already be a String. + + @see String +*/ +class JUCE_API StringRef +{ +public: + /** Creates a StringRef from a raw string literal. + The StringRef object does NOT take ownership or copy this data, so you must + ensure that the data does not change during the lifetime of the StringRef. + Note that this pointer not be null! + */ + StringRef (const char* stringLiteral) noexcept; + + /** Creates a StringRef from a raw char pointer. + The StringRef object does NOT take ownership or copy this data, so you must + ensure that the data does not change during the lifetime of the StringRef. + */ + StringRef (String::CharPointerType stringLiteral) noexcept; + + /** Creates a StringRef from a String. + The StringRef object does NOT take ownership or copy the data from the String, + so you must ensure that the String is not modified or deleted during the lifetime + of the StringRef. + */ + StringRef (const String& string) noexcept; + + /** Creates a StringRef pointer to an empty string. */ + StringRef() noexcept; + + //============================================================================== + /** Returns a raw pointer to the underlying string data. */ + operator const String::CharPointerType::CharType*() const noexcept { return text.getAddress(); } + /** Returns a pointer to the underlying string data as a char pointer object. */ + operator String::CharPointerType() const noexcept { return text; } + + /** Returns true if the string is empty. */ + bool isEmpty() const noexcept { return text.isEmpty(); } + /** Returns true if the string is not empty. */ + bool isNotEmpty() const noexcept { return ! text.isEmpty(); } + /** Returns the number of characters in the string. */ + int length() const noexcept { return (int) text.length(); } + + /** Compares this StringRef with a String. */ + bool operator== (const String& s) const noexcept { return text.compare (s.getCharPointer()) == 0; } + /** Compares this StringRef with a String. */ + bool operator!= (const String& s) const noexcept { return text.compare (s.getCharPointer()) != 0; } + + /** Case-sensitive comparison of two StringRefs. */ + bool operator== (StringRef s) const noexcept { return text.compare (s.text) == 0; } + /** Case-sensitive comparison of two StringRefs. */ + bool operator!= (StringRef s) const noexcept { return text.compare (s.text) != 0; } + + //============================================================================== + /** The text that is referenced. */ + String::CharPointerType text; + + #if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) + // Sorry, non-UTF8 people, you're unable to take advantage of StringRef, because + // you've chosen a character encoding that doesn't match C++ string literals. + String stringCopy; + #endif +}; + +//============================================================================== +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator== (const String& string1, StringRef string2) noexcept; +/** Case-sensitive comparison of two strings. */ +JUCE_API bool JUCE_CALLTYPE operator!= (const String& string1, StringRef string2) noexcept; + +#if JUCE_STRING_UTF_TYPE != 8 && ! defined (DOXYGEN) + inline String operator+ (String s1, StringRef s2) { return s1 += String (s2.text); } +#endif + +#endif // JUCE_STRINGREF_H_INCLUDED diff --git a/source/modules/juce_core/text/juce_TextDiff.cpp b/source/modules/juce_core/text/juce_TextDiff.cpp index da1a47fd4..6b4c80794 100644 --- a/source/modules/juce_core/text/juce_TextDiff.cpp +++ b/source/modules/juce_core/text/juce_TextDiff.cpp @@ -191,10 +191,9 @@ class DiffTests : public UnitTest public: DiffTests() : UnitTest ("TextDiff class") {} - static String createString() + static String createString (Random& r) { juce_wchar buffer[50] = { 0 }; - Random r; for (int i = r.nextInt (49); --i >= 0;) { @@ -224,6 +223,8 @@ public: { beginTest ("TextDiff"); + Random r = getRandom(); + testDiff (String::empty, String::empty); testDiff ("x", String::empty); testDiff (String::empty, "x"); @@ -234,9 +235,9 @@ public: for (int i = 5000; --i >= 0;) { - String s (createString()); - testDiff (s, createString()); - testDiff (s + createString(), s + createString()); + String s (createString (r)); + testDiff (s, createString (r)); + testDiff (s + createString (r), s + createString (r)); } } }; diff --git a/source/modules/juce_core/threads/juce_CriticalSection.h b/source/modules/juce_core/threads/juce_CriticalSection.h index 4228b2c9c..7889b3504 100644 --- a/source/modules/juce_core/threads/juce_CriticalSection.h +++ b/source/modules/juce_core/threads/juce_CriticalSection.h @@ -103,16 +103,16 @@ public: private: //============================================================================== #if JUCE_WINDOWS - // To avoid including windows.h in the public JUCE headers, we'll just allocate a - // block of memory here that's big enough to be used internally as a windows critical - // section structure. + // To avoid including windows.h in the public JUCE headers, we'll just allocate + // a block of memory here that's big enough to be used internally as a windows + // CRITICAL_SECTION structure. #if JUCE_64BIT - uint8 internal [44]; + uint8 lock[44]; #else - uint8 internal [24]; + uint8 lock[24]; #endif #else - mutable pthread_mutex_t internal; + mutable pthread_mutex_t lock; #endif JUCE_DECLARE_NON_COPYABLE (CriticalSection) diff --git a/source/modules/juce_core/threads/juce_Process.h b/source/modules/juce_core/threads/juce_Process.h index 20ad152c7..f3efd6628 100644 --- a/source/modules/juce_core/threads/juce_Process.h +++ b/source/modules/juce_core/threads/juce_Process.h @@ -36,7 +36,7 @@ This contains methods for controlling the current application at the process-level. - @see Thread, JUCEApplication + @see Thread, JUCEApplicationBase */ class JUCE_API Process { @@ -63,7 +63,7 @@ public: immediately - it's intended only for use only when something goes horribly wrong. - @see JUCEApplication::quit + @see JUCEApplicationBase::quit */ static void JUCE_CALLTYPE terminate(); diff --git a/source/modules/juce_core/threads/juce_ReadWriteLock.h b/source/modules/juce_core/threads/juce_ReadWriteLock.h index ab0509960..47bcf0e16 100644 --- a/source/modules/juce_core/threads/juce_ReadWriteLock.h +++ b/source/modules/juce_core/threads/juce_ReadWriteLock.h @@ -59,18 +59,15 @@ public: ReadWriteLock() noexcept; /** Destructor. - - If the object is deleted whilst locked, any subsequent behaviour - is unpredictable. + If the object is deleted whilst locked, any subsequent behaviour is undefined. */ ~ReadWriteLock() noexcept; //============================================================================== /** Locks this object for reading. - Multiple threads can simulaneously lock the object for reading, but if another - thread has it locked for writing, then this will block until it releases the - lock. + Multiple threads can simultaneously lock the object for reading, but if another + thread has it locked for writing, then this will block until it releases the lock. @see exitRead, ScopedReadLock */ @@ -78,7 +75,7 @@ public: /** Tries to lock this object for reading. - Multiple threads can simulaneously lock the object for reading, but if another + Multiple threads can simultaneously lock the object for reading, but if another thread has it locked for writing, then this will fail and return false. @returns true if the lock is successfully gained. diff --git a/source/modules/juce_core/threads/juce_WaitableEvent.h b/source/modules/juce_core/threads/juce_WaitableEvent.h index cf41d9262..83f6f0645 100644 --- a/source/modules/juce_core/threads/juce_WaitableEvent.h +++ b/source/modules/juce_core/threads/juce_WaitableEvent.h @@ -96,7 +96,6 @@ public: //============================================================================== /** Resets the event to an unsignalled state. - If it's not already signalled, this does nothing. */ void reset() const noexcept; @@ -105,7 +104,7 @@ public: private: //============================================================================== #if JUCE_WINDOWS - void* internal; + void* handle; #else mutable pthread_cond_t condition; mutable pthread_mutex_t mutex; diff --git a/source/modules/juce_core/unit_tests/juce_UnitTest.cpp b/source/modules/juce_core/unit_tests/juce_UnitTest.cpp index 9fd6a8e38..f5a60c1a5 100644 --- a/source/modules/juce_core/unit_tests/juce_UnitTest.cpp +++ b/source/modules/juce_core/unit_tests/juce_UnitTest.cpp @@ -26,8 +26,8 @@ ============================================================================== */ -UnitTest::UnitTest (const String& name_) - : name (name_), runner (nullptr) +UnitTest::UnitTest (const String& nm) + : name (nm), runner (nullptr) { getAllTests().add (this); } @@ -46,10 +46,10 @@ Array& UnitTest::getAllTests() void UnitTest::initialise() {} void UnitTest::shutdown() {} -void UnitTest::performTest (UnitTestRunner* const runner_) +void UnitTest::performTest (UnitTestRunner* const newRunner) { - jassert (runner_ != nullptr); - runner = runner_; + jassert (newRunner != nullptr); + runner = newRunner; initialise(); runTest(); @@ -58,22 +58,39 @@ void UnitTest::performTest (UnitTestRunner* const runner_) void UnitTest::logMessage (const String& message) { + // This method's only valid while the test is being run! + jassert (runner != nullptr); + runner->logMessage (message); } void UnitTest::beginTest (const String& testName) { + // This method's only valid while the test is being run! + jassert (runner != nullptr); + runner->beginNewTest (this, testName); } void UnitTest::expect (const bool result, const String& failureMessage) { + // This method's only valid while the test is being run! + jassert (runner != nullptr); + if (result) runner->addPass(); else runner->addFail (failureMessage); } +Random UnitTest::getRandom() const +{ + // This method's only valid while the test is being run! + jassert (runner != nullptr); + + return runner->randomForTest; +} + //============================================================================== UnitTestRunner::UnitTestRunner() : currentTest (nullptr), @@ -110,11 +127,17 @@ void UnitTestRunner::resultsUpdated() { } -void UnitTestRunner::runTests (const Array& tests) +void UnitTestRunner::runTests (const Array& tests, int64 randomSeed) { results.clear(); resultsUpdated(); + if (randomSeed == 0) + randomSeed = Random().nextInt (0x7ffffff); + + randomForTest = Random (randomSeed); + logMessage ("Random seed: 0x" + String::toHexString (randomSeed)); + for (int i = 0; i < tests.size(); ++i) { if (shouldAbortTests()) @@ -133,9 +156,9 @@ void UnitTestRunner::runTests (const Array& tests) endTest(); } -void UnitTestRunner::runAllTests() +void UnitTestRunner::runAllTests (int64 randomSeed) { - runTests (UnitTest::getAllTests()); + runTests (UnitTest::getAllTests(), randomSeed); } void UnitTestRunner::logMessage (const String& message) diff --git a/source/modules/juce_core/unit_tests/juce_UnitTest.h b/source/modules/juce_core/unit_tests/juce_UnitTest.h index cfbb77b8e..f5908c17c 100644 --- a/source/modules/juce_core/unit_tests/juce_UnitTest.h +++ b/source/modules/juce_core/unit_tests/juce_UnitTest.h @@ -163,6 +163,22 @@ public: */ void logMessage (const String& message); + /** Returns a shared RNG that all unit tests should use. + If a test needs random numbers, it's important that when an error is found, the + exact circumstances can be re-created in order to re-test the problem, by + repeating the test with the same random seed value. + To make this possible, the UnitTestRunner class creates a master seed value + for the run, writes this number to the log, and then this method returns a + Random object based on that seed. All tests should only use this method to + create any Random objects that they need. + + Note that this method will return an identical object each time it's called + for a given run, so if you need several different Random objects, the best + way to do that is to call Random::combineSeed() on the result to permute it + with a constant value. + */ + Random getRandom() const; + private: //============================================================================== const String name; @@ -198,13 +214,19 @@ public: The tests are performed in order, and the results are logged. To run all the registered UnitTest objects that exist, use runAllTests(). + + If you want to run the tests with a predetermined seed, you can pass that into + the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. */ - void runTests (const Array& tests); + void runTests (const Array& tests, int64 randomSeed = 0); /** Runs all the UnitTest objects that currently exist. This calls runTests() for all the objects listed in UnitTest::getAllTests(). + + If you want to run the tests with a predetermined seed, you can pass that into + the randomSeed argument, or pass 0 to have a randomly-generated seed chosen. */ - void runAllTests(); + void runAllTests (int64 randomSeed = 0); /** Sets a flag to indicate whether an assertion should be triggered if a test fails. This is true by default. @@ -274,6 +296,7 @@ private: String currentSubCategory; OwnedArray results; bool assertOnFailure, logPasses; + Random randomForTest; void beginNewTest (UnitTest* test, const String& subCategory); void endTest(); diff --git a/source/modules/juce_core/xml/juce_XmlDocument.cpp b/source/modules/juce_core/xml/juce_XmlDocument.cpp index e118b31dc..b95a8a668 100644 --- a/source/modules/juce_core/xml/juce_XmlDocument.cpp +++ b/source/modules/juce_core/xml/juce_XmlDocument.cpp @@ -312,7 +312,8 @@ void XmlDocument::skipNextWhiteSpace() input += closeComment + 3; continue; } - else if (input[1] == '?') + + if (input[1] == '?') { input += 2; const int closeBracket = input.indexOf (CharPointer_ASCII ("?>")); @@ -515,7 +516,8 @@ void XmlDocument::readChildElements (XmlElement* parent) break; } - else if (c1 == '!' && CharacterFunctions::compareUpTo (input + 2, CharPointer_ASCII ("[CDATA["), 7) == 0) + + if (c1 == '!' && CharacterFunctions::compareUpTo (input + 2, CharPointer_ASCII ("[CDATA["), 7) == 0) { input += 9; const String::CharPointerType inputStart (input); diff --git a/source/modules/juce_core/xml/juce_XmlElement.cpp b/source/modules/juce_core/xml/juce_XmlElement.cpp index eb06df3f7..9ec19c54f 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.cpp +++ b/source/modules/juce_core/xml/juce_XmlElement.cpp @@ -42,7 +42,7 @@ XmlElement::XmlAttributeNode::XmlAttributeNode (const String& n, const String& v #endif } -inline bool XmlElement::XmlAttributeNode::hasName (const String& nameToMatch) const noexcept +bool XmlElement::XmlAttributeNode::hasName (StringRef nameToMatch) const noexcept { return name.equalsIgnoreCase (nameToMatch); } @@ -74,9 +74,7 @@ XmlElement& XmlElement::operator= (const XmlElement& other) { removeAllAttributes(); deleteAllChildElements(); - tagName = other.tagName; - copyChildrenAndAttributesFrom (other); } @@ -85,10 +83,10 @@ XmlElement& XmlElement::operator= (const XmlElement& other) #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS XmlElement::XmlElement (XmlElement&& other) noexcept - : nextListItem (static_cast &&> (other.nextListItem)), - firstChildElement (static_cast &&> (other.firstChildElement)), - attributes (static_cast &&> (other.attributes)), - tagName (static_cast (other.tagName)) + : nextListItem (static_cast&&> (other.nextListItem)), + firstChildElement (static_cast&&> (other.firstChildElement)), + attributes (static_cast&&> (other.attributes)), + tagName (static_cast (other.tagName)) { } @@ -99,10 +97,10 @@ XmlElement& XmlElement::operator= (XmlElement&& other) noexcept removeAllAttributes(); deleteAllChildElements(); - nextListItem = static_cast &&> (other.nextListItem); - firstChildElement = static_cast &&> (other.firstChildElement); - attributes = static_cast &&> (other.attributes); - tagName = static_cast (other.tagName); + nextListItem = static_cast&&> (other.nextListItem); + firstChildElement = static_cast&&> (other.firstChildElement); + attributes = static_cast&&> (other.attributes); + tagName = static_cast (other.tagName); return *this; } @@ -163,8 +161,8 @@ namespace XmlOutputFunctions static bool isLegalXmlChar (const uint32 c) noexcept { - static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, 255, 255, 191, 254, 255, 255, 127 }; - + static const unsigned char legalChars[] = { 0, 0, 0, 0, 187, 255, 255, 175, 255, + 255, 255, 191, 254, 255, 255, 127 }; return c < sizeof (legalChars) * 8 && (legalChars [c >> 3] & (1 << (c & 7))) != 0; } @@ -297,10 +295,10 @@ void XmlElement::writeElementAsText (OutputStream& outputStream, } } -String XmlElement::createDocument (const String& dtdToUse, +String XmlElement::createDocument (StringRef dtdToUse, const bool allOnOneLine, const bool includeXmlHeader, - const String& encodingType, + StringRef encodingType, const int lineWrapLength) const { MemoryOutputStream mem (2048); @@ -310,10 +308,10 @@ String XmlElement::createDocument (const String& dtdToUse, } void XmlElement::writeToStream (OutputStream& output, - const String& dtdToUse, + StringRef dtdToUse, const bool allOnOneLine, const bool includeXmlHeader, - const String& encodingType, + StringRef encodingType, const int lineWrapLength) const { using namespace XmlOutputFunctions; @@ -345,8 +343,8 @@ void XmlElement::writeToStream (OutputStream& output, } bool XmlElement::writeToFile (const File& file, - const String& dtdToUse, - const String& encodingType, + StringRef dtdToUse, + StringRef encodingType, const int lineWrapLength) const { TemporaryFile tempFile (file); @@ -364,7 +362,7 @@ bool XmlElement::writeToFile (const File& file, } //============================================================================== -bool XmlElement::hasTagName (const String& possibleTagName) const noexcept +bool XmlElement::hasTagName (StringRef possibleTagName) const noexcept { const bool matches = tagName.equalsIgnoreCase (possibleTagName); @@ -385,12 +383,12 @@ String XmlElement::getTagNameWithoutNamespace() const return tagName.fromLastOccurrenceOf (":", false, false); } -bool XmlElement::hasTagNameIgnoringNamespace (const String& possibleTagName) const +bool XmlElement::hasTagNameIgnoringNamespace (StringRef possibleTagName) const { return hasTagName (possibleTagName) || getTagNameWithoutNamespace() == possibleTagName; } -XmlElement* XmlElement::getNextElementWithTagName (const String& requiredTagName) const +XmlElement* XmlElement::getNextElementWithTagName (StringRef requiredTagName) const { XmlElement* e = nextListItem; @@ -408,89 +406,90 @@ int XmlElement::getNumAttributes() const noexcept const String& XmlElement::getAttributeName (const int index) const noexcept { - const XmlAttributeNode* const att = attributes [index]; - return att != nullptr ? att->name : String::empty; + if (const XmlAttributeNode* const att = attributes [index]) + return att->name; + + return String::empty; } const String& XmlElement::getAttributeValue (const int index) const noexcept { - const XmlAttributeNode* const att = attributes [index]; - return att != nullptr ? att->value : String::empty; + if (const XmlAttributeNode* const att = attributes [index]) + return att->value; + + return String::empty; } -bool XmlElement::hasAttribute (const String& attributeName) const noexcept +XmlElement::XmlAttributeNode* XmlElement::getAttribute (StringRef attributeName) const noexcept { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) + for (XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) if (att->hasName (attributeName)) - return true; + return att; - return false; + return nullptr; +} + +bool XmlElement::hasAttribute (StringRef attributeName) const noexcept +{ + return getAttribute (attributeName) != nullptr; } //============================================================================== -const String& XmlElement::getStringAttribute (const String& attributeName) const noexcept +const String& XmlElement::getStringAttribute (StringRef attributeName) const noexcept { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value; + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value; return String::empty; } -String XmlElement::getStringAttribute (const String& attributeName, const String& defaultReturnValue) const +String XmlElement::getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value; + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value; return defaultReturnValue; } -int XmlElement::getIntAttribute (const String& attributeName, const int defaultReturnValue) const +int XmlElement::getIntAttribute (StringRef attributeName, const int defaultReturnValue) const { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value.getIntValue(); + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value.getIntValue(); return defaultReturnValue; } -double XmlElement::getDoubleAttribute (const String& attributeName, const double defaultReturnValue) const +double XmlElement::getDoubleAttribute (StringRef attributeName, const double defaultReturnValue) const { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return att->value.getDoubleValue(); + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return att->value.getDoubleValue(); return defaultReturnValue; } -bool XmlElement::getBoolAttribute (const String& attributeName, const bool defaultReturnValue) const +bool XmlElement::getBoolAttribute (StringRef attributeName, const bool defaultReturnValue) const { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) + if (const XmlAttributeNode* att = getAttribute (attributeName)) { - if (att->hasName (attributeName)) - { - const juce_wchar firstChar = *(att->value.getCharPointer().findEndOfWhitespace()); + const juce_wchar firstChar = *(att->value.getCharPointer().findEndOfWhitespace()); - return firstChar == '1' - || firstChar == 't' - || firstChar == 'y' - || firstChar == 'T' - || firstChar == 'Y'; - } + return firstChar == '1' + || firstChar == 't' + || firstChar == 'y' + || firstChar == 'T' + || firstChar == 'Y'; } return defaultReturnValue; } -bool XmlElement::compareAttribute (const String& attributeName, - const String& stringToCompareAgainst, +bool XmlElement::compareAttribute (StringRef attributeName, + StringRef stringToCompareAgainst, const bool ignoreCase) const noexcept { - for (const XmlAttributeNode* att = attributes; att != nullptr; att = att->nextListItem) - if (att->hasName (attributeName)) - return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst) - : att->value == stringToCompareAgainst; + if (const XmlAttributeNode* att = getAttribute (attributeName)) + return ignoreCase ? att->value.equalsIgnoreCase (stringToCompareAgainst) + : att->value == stringToCompareAgainst; return false; } @@ -561,7 +560,7 @@ XmlElement* XmlElement::getChildElement (const int index) const noexcept return firstChildElement [index].get(); } -XmlElement* XmlElement::getChildByName (const String& childName) const noexcept +XmlElement* XmlElement::getChildByName (StringRef childName) const noexcept { for (XmlElement* child = firstChildElement; child != nullptr; child = child->nextListItem) if (child->hasTagName (childName)) @@ -576,8 +575,7 @@ void XmlElement::addChildElement (XmlElement* const newNode) noexcept firstChildElement.append (newNode); } -void XmlElement::insertChildElement (XmlElement* const newNode, - int indexToInsertAt) noexcept +void XmlElement::insertChildElement (XmlElement* const newNode, int indexToInsertAt) noexcept { if (newNode != nullptr) { @@ -700,7 +698,7 @@ void XmlElement::deleteAllChildElements() noexcept firstChildElement.deleteAll(); } -void XmlElement::deleteAllChildElementsWithTagName (const String& name) noexcept +void XmlElement::deleteAllChildElementsWithTagName (StringRef name) noexcept { for (XmlElement* child = firstChildElement; child != nullptr;) { @@ -791,8 +789,7 @@ String XmlElement::getAllSubText() const return mem.toUTF8(); } -String XmlElement::getChildElementAllSubText (const String& childTagName, - const String& defaultReturnValue) const +String XmlElement::getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const { if (const XmlElement* const child = getChildByName (childTagName)) return child->getAllSubText(); diff --git a/source/modules/juce_core/xml/juce_XmlElement.h b/source/modules/juce_core/xml/juce_XmlElement.h index 415308882..1a0000887 100644 --- a/source/modules/juce_core/xml/juce_XmlElement.h +++ b/source/modules/juce_core/xml/juce_XmlElement.h @@ -193,10 +193,10 @@ public: determines how lists of attributes get broken up @see writeToStream, writeToFile */ - String createDocument (const String& dtdToUse, + String createDocument (StringRef dtdToUse, bool allOnOneLine = false, bool includeXmlHeader = true, - const String& encodingType = "UTF-8", + StringRef encodingType = "UTF-8", int lineWrapLength = 60) const; /** Writes the document to a stream as UTF-8. @@ -215,10 +215,10 @@ public: @see writeToFile, createDocument */ void writeToStream (OutputStream& output, - const String& dtdToUse, + StringRef dtdToUse, bool allOnOneLine = false, bool includeXmlHeader = true, - const String& encodingType = "UTF-8", + StringRef encodingType = "UTF-8", int lineWrapLength = 60) const; /** Writes the element to a file as an XML document. @@ -241,8 +241,8 @@ public: @see createDocument */ bool writeToFile (const File& destinationFile, - const String& dtdToUse, - const String& encodingType = "UTF-8", + StringRef dtdToUse, + StringRef encodingType = "UTF-8", int lineWrapLength = 60) const; //============================================================================== @@ -250,7 +250,7 @@ public: E.g. for an element such as \, this would return "MOOSE". @see hasTagName */ - inline const String& getTagName() const noexcept { return tagName; } + const String& getTagName() const noexcept { return tagName; } /** Returns the namespace portion of the tag-name, or an empty string if none is specified. */ String getNamespace() const; @@ -262,13 +262,13 @@ public: @param possibleTagName the tag name you're comparing it with @see getTagName */ - bool hasTagName (const String& possibleTagName) const noexcept; + bool hasTagName (StringRef possibleTagName) const noexcept; /** Tests whether this element has a particular tag name, ignoring any XML namespace prefix. So a test for e.g. "xyz" will return true for "xyz" and also "foo:xyz", "bar::xyz", etc. @see getTagName */ - bool hasTagNameIgnoringNamespace (const String& possibleTagName) const; + bool hasTagNameIgnoringNamespace (StringRef possibleTagName) const; //============================================================================== /** Returns the number of XML attributes this element contains. @@ -300,13 +300,13 @@ public: // Attribute-handling methods.. /** Checks whether the element contains an attribute with a certain name. */ - bool hasAttribute (const String& attributeName) const noexcept; + bool hasAttribute (StringRef attributeName) const noexcept; /** Returns the value of a named attribute. @param attributeName the name of the attribute to look up */ - const String& getStringAttribute (const String& attributeName) const noexcept; + const String& getStringAttribute (StringRef attributeName) const noexcept; /** Returns the value of a named attribute. @@ -314,8 +314,7 @@ public: @param defaultReturnValue a value to return if the element doesn't have an attribute with this name */ - String getStringAttribute (const String& attributeName, - const String& defaultReturnValue) const; + String getStringAttribute (StringRef attributeName, const String& defaultReturnValue) const; /** Compares the value of a named attribute with a value passed-in. @@ -325,8 +324,8 @@ public: @returns true if the value of the attribute is the same as the string passed-in; false if it's different (or if no such attribute exists) */ - bool compareAttribute (const String& attributeName, - const String& stringToCompareAgainst, + bool compareAttribute (StringRef attributeName, + StringRef stringToCompareAgainst, bool ignoreCase = false) const noexcept; /** Returns the value of a named attribute as an integer. @@ -339,8 +338,7 @@ public: with this name @see setAttribute */ - int getIntAttribute (const String& attributeName, - int defaultReturnValue = 0) const; + int getIntAttribute (StringRef attributeName, int defaultReturnValue = 0) const; /** Returns the value of a named attribute as floating-point. @@ -352,8 +350,7 @@ public: with this name @see setAttribute */ - double getDoubleAttribute (const String& attributeName, - double defaultReturnValue = 0.0) const; + double getDoubleAttribute (StringRef attributeName, double defaultReturnValue = 0.0) const; /** Returns the value of a named attribute as a boolean. @@ -365,8 +362,7 @@ public: @param defaultReturnValue a value to return if the element doesn't have an attribute with this name */ - bool getBoolAttribute (const String& attributeName, - bool defaultReturnValue = false) const; + bool getBoolAttribute (StringRef attributeName, bool defaultReturnValue = false) const; /** Adds a named attribute to the element. @@ -381,8 +377,7 @@ public: @param newValue the value to set it to @see removeAttribute */ - void setAttribute (const String& attributeName, - const String& newValue); + void setAttribute (const String& attributeName, const String& newValue); /** Adds a named attribute to the element, setting it to an integer value. @@ -396,8 +391,7 @@ public: @param attributeName the name of the attribute to set @param newValue the value to set it to */ - void setAttribute (const String& attributeName, - int newValue); + void setAttribute (const String& attributeName, int newValue); /** Adds a named attribute to the element, setting it to a floating-point value. @@ -411,8 +405,7 @@ public: @param attributeName the name of the attribute to set @param newValue the value to set it to */ - void setAttribute (const String& attributeName, - double newValue); + void setAttribute (const String& attributeName, double newValue); /** Removes a named attribute from the element. @@ -421,17 +414,14 @@ public: */ void removeAttribute (const String& attributeName) noexcept; - /** Removes all attributes from this element. - */ + /** Removes all attributes from this element. */ void removeAllAttributes() noexcept; //============================================================================== // Child element methods.. /** Returns the first of this element's sub-elements. - see getNextElement() for an example of how to iterate the sub-elements. - @see forEachXmlChildElement */ XmlElement* getFirstChildElement() const noexcept { return firstChildElement; } @@ -472,10 +462,9 @@ public: @see getNextElement, forEachXmlChildElementWithTagName */ - XmlElement* getNextElementWithTagName (const String& requiredTagName) const; + XmlElement* getNextElementWithTagName (StringRef requiredTagName) const; /** Returns the number of sub-elements in this element. - @see getChildElement */ int getNumChildElements() const noexcept; @@ -496,7 +485,7 @@ public: @returns the first element with this tag name, or nullptr if none is found @see getNextElement, isTextElement, getChildElement */ - XmlElement* getChildByName (const String& tagNameToLookFor) const noexcept; + XmlElement* getChildByName (StringRef tagNameToLookFor) const noexcept; //============================================================================== /** Appends an element to this element's list of children. @@ -560,16 +549,14 @@ public: bool shouldDeleteTheChild) noexcept; /** Deletes all the child elements in the element. - @see removeChildElement, deleteAllChildElementsWithTagName */ void deleteAllChildElements() noexcept; /** Deletes all the child elements with a given tag name. - @see removeChildElement */ - void deleteAllChildElementsWithTagName (const String& tagName) noexcept; + void deleteAllChildElementsWithTagName (StringRef tagName) noexcept; /** Returns true if the given element is a child of this one. */ bool containsChildElement (const XmlElement* possibleChild) const noexcept; @@ -676,23 +663,20 @@ public: @see getAllSubText */ - String getChildElementAllSubText (const String& childTagName, + String getChildElementAllSubText (StringRef childTagName, const String& defaultReturnValue) const; /** Appends a section of text to this element. - @see isTextElement, getText, getAllSubText */ void addTextElement (const String& text); /** Removes all the text elements from this element. - @see isTextElement, getText, getAllSubText, addTextElement */ void deleteAllTextElements() noexcept; - /** Creates a text element that can be added to a parent element. - */ + /** Creates a text element that can be added to a parent element. */ static XmlElement* createTextElement (const String& text); //============================================================================== @@ -705,7 +689,7 @@ private: LinkedListPointer nextListItem; String name, value; - bool hasName (const String&) const noexcept; + bool hasName (StringRef) const noexcept; private: XmlAttributeNode& operator= (const XmlAttributeNode&); @@ -726,6 +710,7 @@ private: void writeElementAsText (OutputStream&, int indentationLevel, int lineWrapLength) const; void getChildElementsAsArray (XmlElement**) const noexcept; void reorderChildElements (XmlElement**, int) noexcept; + XmlAttributeNode* getAttribute (StringRef) const noexcept; JUCE_LEAK_DETECTOR (XmlElement) }; diff --git a/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp b/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp index 7f99ef5aa..cb9a577eb 100644 --- a/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp +++ b/source/modules/juce_core/zip/juce_GZIPCompressorOutputStream.cpp @@ -169,7 +169,7 @@ public: void runTest() { beginTest ("GZIP"); - Random rng; + Random rng = getRandom(); for (int i = 100; --i >= 0;) { diff --git a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp index 4a72305be..7076b4e47 100644 --- a/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp +++ b/source/modules/juce_core/zip/juce_GZIPDecompressorInputStream.cpp @@ -66,6 +66,10 @@ namespace zlibNamespace #undef Byte #undef fdopen #undef local + #undef Freq + #undef Code + #undef Dad + #undef Len #if JUCE_CLANG #pragma clang diagnostic pop