From 533e7ba7951e914154a4ec1ac19a4b162d14cf08 Mon Sep 17 00:00:00 2001 From: Julian Storer Date: Tue, 22 Feb 2011 15:33:30 +0000 Subject: [PATCH] Many more String changes, so that finally the String class can store its internal data as either utf8, 16 or 32 - this is controlled by a flag JUCE_STRING_UTF_TYPE. It's currently set to utf-8 by default. --- .../Project/jucer_ProjectExport_XCode.h | 2 +- .../Source/Project/jucer_ProjectExporter.h | 2 +- .../Source/MainHostWindow.cpp | 38 +- .../demo/Source/PluginEditor.cpp | 2 +- .../wrapper/RTAS/juce_RTAS_Wrapper.cpp | 8 +- .../wrapper/VST/juce_VST_Wrapper.cpp | 20 +- .../src/model/jucer_BinaryResources.cpp | 6 +- .../src/utility/jucer_UtilityFunctions.cpp | 2 +- .../src/utility/jucer_UtilityFunctions.h | 2 +- src/audio/devices/juce_AudioDeviceManager.cpp | 6 +- .../plugins/formats/juce_VSTPluginFormat.cpp | 12 +- src/core/juce_RelativeTime.cpp | 2 +- src/core/juce_StandardHeader.h | 2 +- src/core/juce_Time.cpp | 50 +- .../juce_CPlusPlusCodeTokeniser.cpp | 11 +- .../code_editor/juce_CodeDocument.cpp | 32 +- .../code_editor/juce_CodeEditorComponent.cpp | 38 +- .../components/controls/juce_TextEditor.cpp | 66 +- src/gui/components/juce_Component.cpp | 2 +- .../components/windows/juce_ComponentPeer.cpp | 2 +- .../juce_LowLevelGraphicsSoftwareRenderer.cpp | 19 +- src/gui/graphics/geometry/juce_Rectangle.h | 2 +- src/io/files/juce_File.cpp | 30 +- src/io/network/juce_MACAddress.cpp | 1 - src/io/streams/juce_MemoryOutputStream.cpp | 2 +- src/memory/juce_MemoryBlock.cpp | 6 +- src/native/linux/juce_linux_Clipboard.cpp | 10 +- src/native/linux/juce_linux_Messaging.cpp | 4 +- src/native/linux/juce_linux_Midi.cpp | 2 +- src/native/linux/juce_linux_Threads.cpp | 2 +- src/native/mac/juce_mac_Strings.mm | 36 +- src/native/windows/juce_win32_Files.cpp | 2 + src/native/windows/juce_win32_Network.cpp | 8 +- src/native/windows/juce_win32_Threads.cpp | 9 +- src/text/juce_CharPointer_UTF16.h | 43 +- src/text/juce_CharPointer_UTF8.h | 51 +- src/text/juce_CharacterFunctions.cpp | 18 - src/text/juce_CharacterFunctions.h | 22 +- src/text/juce_Identifier.h | 4 +- src/text/juce_LocalisedStrings.cpp | 3 +- src/text/juce_String.cpp | 833 +++++++++++------- src/text/juce_String.h | 209 +++-- src/text/juce_StringArray.cpp | 10 +- src/text/juce_StringPool.cpp | 2 +- src/text/juce_StringPool.h | 2 +- src/text/juce_XmlDocument.cpp | 18 +- 46 files changed, 970 insertions(+), 683 deletions(-) diff --git a/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h b/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h index d6f12a7603..6bda1d0d78 100644 --- a/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h +++ b/extras/Jucer (experimental)/Source/Project/jucer_ProjectExport_XCode.h @@ -720,7 +720,7 @@ private: for (int i = 0; i < objects.size(); ++i) { ValueTree& o = *objects.getUnchecked(i); - output << "\t\t" << static_cast (o.getType()) << " = { "; + output << "\t\t" << o.getType().toString() << " = { "; for (int j = 0; j < o.getNumProperties(); ++j) { diff --git a/extras/Jucer (experimental)/Source/Project/jucer_ProjectExporter.h b/extras/Jucer (experimental)/Source/Project/jucer_ProjectExporter.h index 18aa40b4da..92bfeb1bb8 100644 --- a/extras/Jucer (experimental)/Source/Project/jucer_ProjectExporter.h +++ b/extras/Jucer (experimental)/Source/Project/jucer_ProjectExporter.h @@ -92,7 +92,7 @@ public: const String getExporterIdentifierMacro() const { - return "JUCER_" + settings.getType() + "_" + return "JUCER_" + settings.getType().toString() + "_" + String::toHexString (settings [Ids::targetFolder].toString().hashCode()).toUpperCase(); } diff --git a/extras/audio plugin host/Source/MainHostWindow.cpp b/extras/audio plugin host/Source/MainHostWindow.cpp index ed7c0b92a6..b5abd56ea9 100644 --- a/extras/audio plugin host/Source/MainHostWindow.cpp +++ b/extras/audio plugin host/Source/MainHostWindow.cpp @@ -80,7 +80,7 @@ MainHostWindow::MainHostWindow() DocumentWindow::allButtons) { XmlElement* const savedAudioState = ApplicationProperties::getInstance()->getUserSettings() - ->getXmlValue (T("audioDeviceState")); + ->getXmlValue ("audioDeviceState"); deviceManager.initialise (256, 256, savedAudioState, true); @@ -101,7 +101,7 @@ MainHostWindow::MainHostWindow() XmlElement* const savedPluginList = ApplicationProperties::getInstance() ->getUserSettings() - ->getXmlValue (T("pluginList")); + ->getXmlValue ("pluginList"); if (savedPluginList != 0) { @@ -110,7 +110,7 @@ MainHostWindow::MainHostWindow() } pluginSortMethod = (KnownPluginList::SortMethod) ApplicationProperties::getInstance()->getUserSettings() - ->getIntValue (T("pluginSortMethod"), KnownPluginList::sortByManufacturer); + ->getIntValue ("pluginSortMethod", KnownPluginList::sortByManufacturer); knownPluginList.addChangeListener (this); @@ -169,7 +169,7 @@ void MainHostWindow::changeListenerCallback (ChangeBroadcaster*) if (savedPluginList != 0) { ApplicationProperties::getInstance()->getUserSettings() - ->setValue (T("pluginList"), savedPluginList); + ->setValue ("pluginList", savedPluginList); delete savedPluginList; @@ -199,7 +199,7 @@ const PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const St PopupMenu recentFilesMenu; recentFiles.createPopupMenuItems (recentFilesMenu, 100, true, true); - menu.addSubMenu (T("Open recent file"), recentFilesMenu); + menu.addSubMenu ("Open recent file", recentFilesMenu); menu.addCommandItem (commandManager, CommandIDs::save); menu.addCommandItem (commandManager, CommandIDs::saveAs); @@ -211,9 +211,9 @@ const PopupMenu MainHostWindow::getMenuForIndex (int topLevelMenuIndex, const St // "Plugins" menu PopupMenu pluginsMenu; addPluginsToMenu (pluginsMenu); - menu.addSubMenu (T("Create plugin"), pluginsMenu); + menu.addSubMenu ("Create plugin", pluginsMenu); menu.addSeparator(); - menu.addItem (250, T("Delete all plugins")); + menu.addItem (250, "Delete all plugins"); } else if (topLevelMenuIndex == 2) @@ -272,7 +272,7 @@ void MainHostWindow::menuItemSelected (int menuItemID, int /*topLevelMenuIndex*/ pluginSortMethod = KnownPluginList::sortByFileSystemLocation; ApplicationProperties::getInstance()->getUserSettings() - ->setValue (T("pluginSortMethod"), (int) pluginSortMethod); + ->setValue ("pluginSortMethod", (int) pluginSortMethod); } else { @@ -339,34 +339,34 @@ void MainHostWindow::getCommandInfo (const CommandID commandID, ApplicationComma switch (commandID) { case CommandIDs::open: - result.setInfo (T("Open..."), - T("Opens a filter graph file"), + result.setInfo ("Open...), + "Opens a filter graph file), category, 0); - result.defaultKeypresses.add (KeyPress (T('o'), ModifierKeys::commandModifier, 0)); + result.defaultKeypresses.add (KeyPress ('o), ModifierKeys::commandModifier, 0)); break; case CommandIDs::save: - result.setInfo (T("Save"), - T("Saves the current graph to a file"), + result.setInfo ("Save", + "Saves the current graph to a file", category, 0); - result.defaultKeypresses.add (KeyPress (T('s'), ModifierKeys::commandModifier, 0)); + result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::commandModifier, 0)); break; case CommandIDs::saveAs: - result.setInfo (T("Save As..."), - T("Saves a copy of the current graph to a file"), + result.setInfo ("Save As...), + "Saves a copy of the current graph to a file", category, 0); - result.defaultKeypresses.add (KeyPress (T('s'), ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0)); + result.defaultKeypresses.add (KeyPress ('s', ModifierKeys::shiftModifier | ModifierKeys::commandModifier, 0)); break; case CommandIDs::showPluginListEditor: result.setInfo ("Edit the list of available plug-Ins...", String::empty, category, 0); - result.addDefaultKeypress (T('p'), ModifierKeys::commandModifier); + result.addDefaultKeypress ('p', ModifierKeys::commandModifier); break; case CommandIDs::showAudioSettings: result.setInfo ("Change the audio device settings", String::empty, category, 0); - result.addDefaultKeypress (T('a'), ModifierKeys::commandModifier); + result.addDefaultKeypress ('a', ModifierKeys::commandModifier); break; case CommandIDs::aboutBox: diff --git a/extras/audio plugins/demo/Source/PluginEditor.cpp b/extras/audio plugins/demo/Source/PluginEditor.cpp index b8b7e5c432..32a0477d4e 100644 --- a/extras/audio plugins/demo/Source/PluginEditor.cpp +++ b/extras/audio plugins/demo/Source/PluginEditor.cpp @@ -161,7 +161,7 @@ void JuceDemoPluginAudioProcessorEditor::displayPositionInfo (const AudioPlayHea { lastDisplayedPosition = pos; String displayText; - displayText.preallocateStorage (64); + displayText.preallocateBytes (128); displayText << String (pos.bpm, 2) << " bpm, " << pos.timeSigNumerator << '/' << pos.timeSigDenominator diff --git a/extras/audio plugins/wrapper/RTAS/juce_RTAS_Wrapper.cpp b/extras/audio plugins/wrapper/RTAS/juce_RTAS_Wrapper.cpp index eaa2e7fd85..dec9bf1ef5 100644 --- a/extras/audio plugins/wrapper/RTAS/juce_RTAS_Wrapper.cpp +++ b/extras/audio plugins/wrapper/RTAS/juce_RTAS_Wrapper.cpp @@ -885,7 +885,7 @@ private: void GetNameOfLength (char* name, int maxLength, OSType inControllerType) const { - juceFilter->getParameterName (index).copyToCString (name, maxLength); + juceFilter->getParameterName (index).copyToUTF8 (name, maxLength); } long GetPriority() const { return kFicCooperativeTaskPriority; } @@ -900,7 +900,7 @@ private: void GetValueString (char* valueString, int maxLength, long value) const { - juceFilter->getParameterText (index).copyToCString (valueString, maxLength); + juceFilter->getParameterText (index).copyToUTF8 (valueString, maxLength); } Cmn_Bool IsAutomatable() const @@ -925,7 +925,7 @@ public: JucePlugInGroup() { DefineManufacturerNamesAndID (JucePlugin_Manufacturer, JucePlugin_RTASManufacturerCode); - DefinePlugInNamesAndVersion (createRTASName().toCString(), JucePlugin_VersionCode); + DefinePlugInNamesAndVersion (createRTASName().toUTF8(), JucePlugin_VersionCode); #ifndef JUCE_DEBUG AddGestalt (pluginGestalt_IsCacheable); @@ -955,7 +955,7 @@ public: JucePlugin_RTASProductId, JucePlugin_RTASCategory); - type->DefineTypeNames (createRTASName().toCString()); + type->DefineTypeNames (createRTASName().toUTF8().getAddress()); type->DefineSampleRateSupport (eSupports48kAnd96kAnd192k); type->DefineStemFormats (getFormatForChans (channelConfigs [i][0] != 0 ? channelConfigs [i][0] : channelConfigs [i][1]), diff --git a/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp b/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp index da0431869d..b29df3bfae 100644 --- a/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp +++ b/extras/audio plugins/wrapper/VST/juce_VST_Wrapper.cpp @@ -410,13 +410,13 @@ public: //============================================================================== bool getEffectName (char* name) { - String (JucePlugin_Name).copyToCString (name, 64); + String (JucePlugin_Name).copyToUTF8 (name, 64); return true; } bool getVendorString (char* text) { - String (JucePlugin_Manufacturer).copyToCString (text, 64); + String (JucePlugin_Manufacturer).copyToUTF8 (text, 64); return true; } @@ -486,8 +486,8 @@ public: const String name (filter->getInputChannelName ((int) index)); - name.copyToCString (properties->label, kVstMaxLabelLen - 1); - name.copyToCString (properties->shortLabel, kVstMaxShortLabelLen - 1); + name.copyToUTF8 (properties->label, kVstMaxLabelLen - 1); + name.copyToUTF8 (properties->shortLabel, kVstMaxShortLabelLen - 1); if (speakerIn != kSpeakerArrEmpty) { @@ -514,8 +514,8 @@ public: const String name (filter->getOutputChannelName ((int) index)); - name.copyToCString (properties->label, kVstMaxLabelLen - 1); - name.copyToCString (properties->shortLabel, kVstMaxShortLabelLen - 1); + name.copyToUTF8 (properties->label, kVstMaxLabelLen - 1); + name.copyToUTF8 (properties->shortLabel, kVstMaxShortLabelLen - 1); if (speakerOut != kSpeakerArrEmpty) { @@ -851,14 +851,14 @@ public: void getProgramName (char* name) { if (filter != 0) - filter->getProgramName (filter->getCurrentProgram()).copyToCString (name, 24); + filter->getProgramName (filter->getCurrentProgram()).copyToUTF8 (name, 24); } bool getProgramNameIndexed (VstInt32 /*category*/, VstInt32 index, char* text) { if (filter != 0 && isPositiveAndBelow (index, filter->getNumPrograms())) { - filter->getProgramName (index).copyToCString (text, 24); + filter->getProgramName (index).copyToUTF8 (text, 24); return true; } @@ -889,7 +889,7 @@ public: if (filter != 0) { jassert (isPositiveAndBelow (index, filter->getNumParameters())); - filter->getParameterText (index).copyToCString (text, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. + filter->getParameterText (index).copyToUTF8 (text, 24); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. } } @@ -898,7 +898,7 @@ public: if (filter != 0) { jassert (isPositiveAndBelow (index, filter->getNumParameters())); - filter->getParameterName (index).copyToCString (text, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. + filter->getParameterName (index).copyToUTF8 (text, 16); // length should technically be kVstMaxParamStrLen, which is 8, but hosts will normally allow a bit more. } } diff --git a/extras/the jucer/src/model/jucer_BinaryResources.cpp b/extras/the jucer/src/model/jucer_BinaryResources.cpp index 620ce02ae0..b2f9f21bd5 100644 --- a/extras/the jucer/src/model/jucer_BinaryResources.cpp +++ b/extras/the jucer/src/model/jucer_BinaryResources.cpp @@ -243,12 +243,12 @@ void BinaryResources::loadFromCpp (const File& cppFileLocation, const String& cp .fromFirstOccurrenceOf (T("{"), false, false)); MemoryOutputStream out; - const juce_wchar* t = (const juce_wchar*) dataString; + String::CharPointerType t (dataString.getCharPointer()); int n = 0; - while (*t != 0) + while (! t.isEmpty()) { - const juce_wchar c = *t++; + const juce_wchar c = t.getAndAdvance(); if (c >= '0' && c <= '9') n = n * 10 + (c - '0'); diff --git a/extras/the jucer/src/utility/jucer_UtilityFunctions.cpp b/extras/the jucer/src/utility/jucer_UtilityFunctions.cpp index bc15a08316..3ecae14e88 100644 --- a/extras/the jucer/src/utility/jucer_UtilityFunctions.cpp +++ b/extras/the jucer/src/utility/jucer_UtilityFunctions.cpp @@ -33,7 +33,7 @@ const String replaceCEscapeChars (const String& s) const int len = s.length(); String r; - r.preallocateStorage (len + 2); + r.preallocateBytes (4 * len + 4); bool lastWasHexEscapeCode = false; for (int i = 0; i < len; ++i) diff --git a/extras/the jucer/src/utility/jucer_UtilityFunctions.h b/extras/the jucer/src/utility/jucer_UtilityFunctions.h index 8ceb9e73e6..9e6f59a116 100644 --- a/extras/the jucer/src/utility/jucer_UtilityFunctions.h +++ b/extras/the jucer/src/utility/jucer_UtilityFunctions.h @@ -148,7 +148,7 @@ public: const String toString() const throw() { String s; - s.preallocateStorage (12); + s.preallocateBytes (32); addPosDescription (s, xMode, x); s << ' '; addPosDescription (s, yMode, y); s << ' '; addSizeDescription (s, wMode, w); s << ' '; diff --git a/src/audio/devices/juce_AudioDeviceManager.cpp b/src/audio/devices/juce_AudioDeviceManager.cpp index 2c999bbf23..a59d388cc4 100644 --- a/src/audio/devices/juce_AudioDeviceManager.cpp +++ b/src/audio/devices/juce_AudioDeviceManager.cpp @@ -613,7 +613,7 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat { const ScopedLock sl (audioCallbackLock); - if (inputLevelMeasurementEnabledCount > 0) + if (inputLevelMeasurementEnabledCount > 0 && numInputChannels > 0) { for (int j = 0; j < numSamples; ++j) { @@ -634,6 +634,10 @@ void AudioDeviceManager::audioDeviceIOCallbackInt (const float** inputChannelDat inputLevel = 0; } } + else + { + inputLevel = 0; + } if (callbacks.size() > 0) { diff --git a/src/audio/plugins/formats/juce_VSTPluginFormat.cpp b/src/audio/plugins/formats/juce_VSTPluginFormat.cpp index f442197d75..7eb4cf295e 100644 --- a/src/audio/plugins/formats/juce_VSTPluginFormat.cpp +++ b/src/audio/plugins/formats/juce_VSTPluginFormat.cpp @@ -483,7 +483,7 @@ public: if (file.hasFileExtension (".vst")) { - const char* const utf8 = filename.toUTF8(); + const char* const utf8 = filename.toUTF8().getAddress(); CFURLRef url = CFURLCreateFromFileSystemRepresentation (0, (const UInt8*) utf8, strlen (utf8), file.isDirectory()); @@ -2014,7 +2014,7 @@ void VSTPluginInstance::setParamsInProgramBlock (fxProgram* const prog) prog->fxVersion = vst_swap (getVersionNumber()); prog->numParams = vst_swap (numParams); - getCurrentProgramName().copyToCString (prog->prgName, sizeof (prog->prgName) - 1); + getCurrentProgramName().copyToUTF8 (prog->prgName, sizeof (prog->prgName) - 1); for (int i = 0; i < numParams; ++i) prog->params[i] = vst_swapFloat (getParameter (i)); @@ -2065,7 +2065,7 @@ bool VSTPluginInstance::saveToFXBFile (MemoryBlock& dest, bool isFXB, int maxSiz set->numPrograms = vst_swap (numPrograms); set->chunkSize = vst_swap ((long) chunk.getSize()); - getCurrentProgramName().copyToCString (set->name, sizeof (set->name) - 1); + getCurrentProgramName().copyToUTF8 (set->name, sizeof (set->name) - 1); chunk.copyTo (set->chunk, 0, chunk.getSize()); } } @@ -2230,7 +2230,7 @@ namespace if (JUCEApplication::getInstance() != 0) hostName = JUCEApplication::getInstance()->getApplicationName(); - hostName.copyToCString ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); + hostName.copyToUTF8 ((char*) ptr, jmin (kVstMaxVendorStrLen, kVstMaxProductStrLen) - 1); break; } @@ -2544,7 +2544,7 @@ void VSTPluginInstance::createTempParameterStore (MemoryBlock& dest) dest.setSize (64 + 4 * getNumParameters()); dest.fillWith (0); - getCurrentProgramName().copyToCString ((char*) dest.getData(), 63); + getCurrentProgramName().copyToUTF8 ((char*) dest.getData(), 63); float* const p = (float*) (((char*) dest.getData()) + 64); for (int i = 0; i < getNumParameters(); ++i) @@ -2594,7 +2594,7 @@ void VSTPluginInstance::changeProgramName (int index, const String& newName) if (index == getCurrentProgram()) { if (getNumPrograms() > 0 && newName != getCurrentProgramName()) - dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toCString(), 0.0f); + dispatch (effSetProgramName, 0, 0, (void*) newName.substring (0, 24).toUTF8().getAddress(), 0.0f); } else { diff --git a/src/core/juce_RelativeTime.cpp b/src/core/juce_RelativeTime.cpp index 433e97d92c..2bbeb6dc65 100644 --- a/src/core/juce_RelativeTime.cpp +++ b/src/core/juce_RelativeTime.cpp @@ -68,7 +68,7 @@ const String RelativeTime::getDescription (const String& returnValueForZeroTime) return returnValueForZeroTime; String result; - result.preallocateStorage (16); + result.preallocateBytes (32); if (seconds < 0) result << '-'; diff --git a/src/core/juce_StandardHeader.h b/src/core/juce_StandardHeader.h index 86ba62bd06..f54275afa9 100644 --- a/src/core/juce_StandardHeader.h +++ b/src/core/juce_StandardHeader.h @@ -33,7 +33,7 @@ */ #define JUCE_MAJOR_VERSION 1 #define JUCE_MINOR_VERSION 53 -#define JUCE_BUILDNUMBER 35 +#define JUCE_BUILDNUMBER 36 /** Current Juce version number. diff --git a/src/core/juce_Time.cpp b/src/core/juce_Time.cpp index 2f1fcb93e5..ae411813fa 100644 --- a/src/core/juce_Time.cpp +++ b/src/core/juce_Time.cpp @@ -56,7 +56,7 @@ BEGIN_JUCE_NAMESPACE //============================================================================== namespace TimeHelpers { - static struct tm millisToLocal (const int64 millis) throw() + struct tm millisToLocal (const int64 millis) throw() { struct tm result; const int64 seconds = millis / 1000; @@ -92,30 +92,51 @@ namespace TimeHelpers { time_t now = static_cast (seconds); - #if JUCE_WINDOWS - #ifdef USE_NEW_SECURE_TIME_FNS + #if JUCE_WINDOWS + #ifdef USE_NEW_SECURE_TIME_FNS if (now >= 0 && now <= 0x793406fff) localtime_s (&result, &now); else zeromem (&result, sizeof (result)); - #else + #else result = *localtime (&now); - #endif - #else - // more thread-safe - localtime_r (&now, &result); - #endif + #endif + #else + + localtime_r (&now, &result); // more thread-safe + #endif } return result; } - static int extendedModulo (const int64 value, const int modulo) throw() + int extendedModulo (const int64 value, const int modulo) throw() { return (int) (value >= 0 ? (value % modulo) : (value - ((value / modulo) + 1) * modulo)); } + int doFTime (juce_wchar* const dest, const int maxChars, const String& format, const struct tm* const tm) throw() + { + #if JUCE_ANDROID + HeapBlock tempDest; + tempDest.calloc (maxChars + 2); + const int result = (int) strftime (tempDest, maxChars, format.toUTF8(), tm); + if (result > 0) + CharPointer_UTF32 (dest).writeAll (CharPointer_UTF8 (tempDest.getData())); + return result; + #elif JUCE_WINDOWS + HeapBlock tempDest; + tempDest.calloc (maxChars + 2); + const int result = (int) wcsftime (tempDest, maxChars, format.toUTF16(), tm); + if (result > 0) + CharPointer_UTF32 (dest).writeAll (CharPointer_UTF16 (tempDest.getData())); + return result; + #else + return (int) wcsftime (dest, maxChars, format.toUTF32(), tm); + #endif + } + static uint32 lastMSCounterValue = 0; } @@ -342,19 +363,18 @@ const String Time::toString (const bool includeDate, const String Time::formatted (const String& format) const { - String buffer; int bufferSize = 128; - buffer.preallocateStorage (bufferSize); + HeapBlock buffer (128); struct tm t (TimeHelpers::millisToLocal (millisSinceEpoch)); - while (CharacterFunctions::ftime (buffer.getCharPointer().getAddress(), bufferSize, format.getCharPointer(), &t) <= 0) + while (TimeHelpers::doFTime (buffer.getData(), bufferSize, format, &t) <= 0) { bufferSize += 128; - buffer.preallocateStorage (bufferSize); + buffer.malloc (bufferSize); } - return buffer; + return String (buffer); } //============================================================================== diff --git a/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp b/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp index d9c2a8e13c..71522872ec 100644 --- a/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp +++ b/src/gui/components/code_editor/juce_CPlusPlusCodeTokeniser.cpp @@ -115,23 +115,24 @@ namespace CppTokeniser int parseIdentifier (CodeDocument::Iterator& source) throw() { int tokenLength = 0; - juce_wchar possibleIdentifier [19]; + String::CharPointerType::CharType possibleIdentifier [100]; + String::CharPointerType possible (possibleIdentifier); while (isIdentifierBody (source.peekNextChar())) { const juce_wchar c = source.nextChar(); - if (tokenLength < numElementsInArray (possibleIdentifier) - 1) - possibleIdentifier [tokenLength] = c; + if (tokenLength < 20) + possible.write (c); ++tokenLength; } if (tokenLength > 1 && tokenLength <= 16) { - possibleIdentifier [tokenLength] = 0; + possible.writeNull(); - if (isReservedKeyword (CharPointer_UTF32 (possibleIdentifier), tokenLength)) + if (isReservedKeyword (String::CharPointerType (possibleIdentifier), tokenLength)) return CPlusPlusCodeTokeniser::tokenType_builtInKeyword; } diff --git a/src/gui/components/code_editor/juce_CodeDocument.cpp b/src/gui/components/code_editor/juce_CodeDocument.cpp index 21530a7c5a..9678b7d8d0 100644 --- a/src/gui/components/code_editor/juce_CodeDocument.cpp +++ b/src/gui/components/code_editor/juce_CodeDocument.cpp @@ -28,6 +28,7 @@ BEGIN_JUCE_NAMESPACE #include "juce_CodeDocument.h" +#include "../../../io/streams/juce_MemoryOutputStream.h" //============================================================================== @@ -107,13 +108,19 @@ public: void updateLength() throw() { - lineLengthWithoutNewLines = lineLength = line.length(); + lineLength = 0; + lineLengthWithoutNewLines = 0; - while (lineLengthWithoutNewLines > 0 - && (line [lineLengthWithoutNewLines - 1] == '\n' - || line [lineLengthWithoutNewLines - 1] == '\r')) + String::CharPointerType t (line.getCharPointer()); + + while (! t.isEmpty()) { - --lineLengthWithoutNewLines; + ++lineLength; + + const juce_wchar c = t.getAndAdvance(); + + if (c != '\n' && c != '\r') + lineLengthWithoutNewLines = lineLength; } } @@ -201,7 +208,7 @@ void CodeDocument::Iterator::skipToEndOfLine() juce_wchar CodeDocument::Iterator::peekNextChar() const { - if (currentLine == 0) + if (currentLine == 0 || currentLine->line.isEmpty()) return 0; jassert (currentLine == document->lines.getUnchecked (line)); @@ -481,9 +488,8 @@ const String CodeDocument::getTextBetween (const Position& start, const Position return (line == 0) ? String::empty : line->line.substring (start.getIndexInLine(), end.getIndexInLine()); } - String result; - result.preallocateStorage (end.getPosition() - start.getPosition() + 4); - String::Concatenator concatenator (result); + MemoryOutputStream mo; + mo.preallocate (end.getPosition() - start.getPosition() + 4); const int maxLine = jmin (lines.size() - 1, endLine); @@ -495,20 +501,20 @@ const String CodeDocument::getTextBetween (const Position& start, const Position if (i == startLine) { const int index = start.getIndexInLine(); - concatenator.append (line->line.substring (index, len)); + mo << line->line.substring (index, len); } else if (i == endLine) { len = end.getIndexInLine(); - concatenator.append (line->line.substring (0, len)); + mo << line->line.substring (0, len); } else { - concatenator.append (line->line); + mo << line->line; } } - return result; + return mo.toString(); } int CodeDocument::getNumCharacters() const throw() diff --git a/src/gui/components/code_editor/juce_CodeEditorComponent.cpp b/src/gui/components/code_editor/juce_CodeEditorComponent.cpp index 233b41c88a..d13d520a89 100644 --- a/src/gui/components/code_editor/juce_CodeEditorComponent.cpp +++ b/src/gui/components/code_editor/juce_CodeEditorComponent.cpp @@ -260,10 +260,11 @@ private: { jassert (index <= line.length()); + String::CharPointerType t (line.getCharPointer()); int col = 0; for (int i = 0; i < index; ++i) { - if (line[i] != '\t') + if (t.getAndAdvance() != '\t') ++col; else col += spacesPerTab - (col % spacesPerTab); @@ -777,12 +778,18 @@ namespace CodeEditorHelpers { int findFirstNonWhitespaceChar (const String& line) throw() { - const int len = line.length(); + String::CharPointerType t (line.getCharPointer()); + int i = 0; - for (int i = 0; i < len; ++i) - if (! CharacterFunctions::isWhitespace (line [i])) + while (! t.isEmpty()) + { + if (! t.isWhitespace()) return i; + ++t; + ++i; + } + return 0; } } @@ -1119,13 +1126,18 @@ void CodeEditorComponent::setTabSize (const int numSpaces, const bool insertSpac int CodeEditorComponent::indexToColumn (int lineNum, int index) const throw() { - const String line (document.getLine (lineNum)); - jassert (index <= line.length()); + String::CharPointerType t (document.getLine (lineNum).getCharPointer()); int col = 0; for (int i = 0; i < index; ++i) { - if (line[i] != '\t') + if (t.isEmpty()) + { + jassertfalse; + break; + } + + if (t.getAndAdvance() != '\t') ++col; else col += getTabSize() - (col % getTabSize()); @@ -1136,19 +1148,21 @@ int CodeEditorComponent::indexToColumn (int lineNum, int index) const throw() int CodeEditorComponent::columnToIndex (int lineNum, int column) const throw() { - const String line (document.getLine (lineNum)); - const int lineLength = line.length(); + String::CharPointerType t (document.getLine (lineNum).getCharPointer()); - int i, col = 0; - for (i = 0; i < lineLength; ++i) + int i = 0, col = 0; + + while (! t.isEmpty()) { - if (line[i] != '\t') + if (t.getAndAdvance() != '\t') ++col; else col += getTabSize() - (col % getTabSize()); if (col > column) break; + + ++i; } return i; diff --git a/src/gui/components/controls/juce_TextEditor.cpp b/src/gui/components/controls/juce_TextEditor.cpp index 77b14123f1..9ff8094072 100644 --- a/src/gui/components/controls/juce_TextEditor.cpp +++ b/src/gui/components/controls/juce_TextEditor.cpp @@ -33,6 +33,7 @@ BEGIN_JUCE_NAMESPACE #include "../../../utilities/juce_SystemClipboard.h" #include "../../../core/juce_Time.h" #include "../../../text/juce_LocalisedStrings.h" +#include "../../../io/streams/juce_MemoryOutputStream.h" #include "../lookandfeel/juce_LookAndFeel.h" @@ -207,14 +208,13 @@ public: return section2; } - void appendAllText (String::Concatenator& concatenator) const + void appendAllText (MemoryOutputStream& mo) const { for (int i = 0; i < atoms.size(); ++i) - concatenator.append (getAtom(i)->atomText); + mo << getAtom(i)->atomText; } - void appendSubstring (String::Concatenator& concatenator, - const Range& range) const + void appendSubstring (MemoryOutputStream& mo, const Range& range) const { int index = 0; for (int i = 0; i < atoms.size(); ++i) @@ -230,7 +230,7 @@ public: const Range r ((range - index).getIntersectionWith (Range (0, (int) atom->numChars))); if (! r.isEmpty()) - concatenator.append (atom->atomText.substring (r.getStart(), r.getEnd())); + mo << atom->atomText.substring (r.getStart(), r.getEnd()); } index = nextIndex; @@ -1834,7 +1834,8 @@ void TextEditor::mouseDoubleClick (const MouseEvent& e) while (tokenEnd < totalLength) { // (note the slight bodge here - it's because iswalnum only checks for alphabetic chars in the current locale) - if (CharacterFunctions::isLetterOrDigit (t [tokenEnd]) || t [tokenEnd] > 128) + const juce_wchar c = t [tokenEnd]; + if (CharacterFunctions::isLetterOrDigit (c) || c > 128) ++tokenEnd; else break; @@ -1845,7 +1846,8 @@ void TextEditor::mouseDoubleClick (const MouseEvent& e) while (tokenStart > 0) { // (note the slight bodge here - it's because iswalnum only checks for alphabetic chars in the current locale) - if (CharacterFunctions::isLetterOrDigit (t [tokenStart - 1]) || t [tokenStart - 1] > 128) + const juce_wchar c = t [tokenStart - 1]; + if (CharacterFunctions::isLetterOrDigit (c) || c > 128) --tokenStart; else break; @@ -1855,7 +1857,8 @@ void TextEditor::mouseDoubleClick (const MouseEvent& e) { while (tokenEnd < totalLength) { - if (t [tokenEnd] != '\r' && t [tokenEnd] != '\n') + const juce_wchar c = t [tokenEnd]; + if (c != '\r' && c != '\n') ++tokenEnd; else break; @@ -1863,7 +1866,8 @@ void TextEditor::mouseDoubleClick (const MouseEvent& e) while (tokenStart > 0) { - if (t [tokenStart - 1] != '\r' && t [tokenStart - 1] != '\n') + const juce_wchar c = t [tokenStart - 1]; + if (c != '\r' && c != '\n') --tokenStart; else break; @@ -2426,44 +2430,42 @@ void TextEditor::remove (const Range& range, //============================================================================== const String TextEditor::getText() const { - String t; - t.preallocateStorage (getTotalNumChars()); - String::Concatenator concatenator (t); + MemoryOutputStream mo; + mo.preallocate (getTotalNumChars()); for (int i = 0; i < sections.size(); ++i) - sections.getUnchecked (i)->appendAllText (concatenator); + sections.getUnchecked (i)->appendAllText (mo); - return t; + return mo.toString(); } const String TextEditor::getTextInRange (const Range& range) const { - String t; + if (range.isEmpty()) + return String::empty; - if (! range.isEmpty()) - { - t.preallocateStorage (jmin (getTotalNumChars(), range.getLength())); - String::Concatenator concatenator (t); - int index = 0; + MemoryOutputStream mo; + mo.preallocate (jmin (getTotalNumChars(), range.getLength())); - for (int i = 0; i < sections.size(); ++i) - { - const UniformTextSection* const s = sections.getUnchecked (i); - const int nextIndex = index + s->getTotalLength(); + int index = 0; - if (range.getStart() < nextIndex) - { - if (range.getEnd() <= index) - break; + for (int i = 0; i < sections.size(); ++i) + { + const UniformTextSection* const s = sections.getUnchecked (i); + const int nextIndex = index + s->getTotalLength(); - s->appendSubstring (concatenator, range - index); - } + if (range.getStart() < nextIndex) + { + if (range.getEnd() <= index) + break; - index = nextIndex; + s->appendSubstring (mo, range - index); } + + index = nextIndex; } - return t; + return mo.toString(); } const String TextEditor::getHighlightedText() const diff --git a/src/gui/components/juce_Component.cpp b/src/gui/components/juce_Component.cpp index 978f23ec7c..1a2375b38e 100644 --- a/src/gui/components/juce_Component.cpp +++ b/src/gui/components/juce_Component.cpp @@ -224,7 +224,7 @@ public: static const Identifier getColourPropertyId (const int colourId) { String s; - s.preallocateStorage (18); + s.preallocateBytes (32); s << "jcclr_" << String::toHexString (colourId); return s; } diff --git a/src/gui/components/windows/juce_ComponentPeer.cpp b/src/gui/components/windows/juce_ComponentPeer.cpp index ea866ea233..f33eaf3b68 100644 --- a/src/gui/components/windows/juce_ComponentPeer.cpp +++ b/src/gui/components/windows/juce_ComponentPeer.cpp @@ -497,7 +497,7 @@ public: void messageCallback() { - if (target != 0) + if (target.get() != 0) dropTarget->filesDropped (files, position.getX(), position.getY()); } diff --git a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp index a27ce89be4..971f05a294 100644 --- a/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp +++ b/src/gui/graphics/contexts/juce_LowLevelGraphicsSoftwareRenderer.cpp @@ -2079,6 +2079,18 @@ public: } } + void drawGlyph (const Font& f, int glyphNumber, const AffineTransform& transform) + { + const ScopedPointer et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, getTransformWith (transform))); + + if (et != 0) + { + SoftwareRendererClasses::ClipRegion_EdgeTable* edgeTableClip = new SoftwareRendererClasses::ClipRegion_EdgeTable (*et); + SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill (edgeTableClip); + fillShape (shapeToFill, false); + } + } + void fillShape (SoftwareRendererClasses::ClipRegionBase::Ptr shapeToFill, const bool replaceContents) { jassert (clip != 0); @@ -2523,11 +2535,8 @@ void LowLevelGraphicsSoftwareRenderer::drawGlyph (int glyphNumber, const AffineT else { const float fontHeight = f.getHeight(); - const ScopedPointer et (f.getTypeface()->getEdgeTableForGlyph (glyphNumber, - AffineTransform::scale (fontHeight * f.getHorizontalScale(), fontHeight) - .followedBy (transform))); - if (et != 0) - currentState->fillEdgeTable (*et, 0.0f, 0); + currentState->drawGlyph (f, glyphNumber, AffineTransform::scale (fontHeight * f.getHorizontalScale(), fontHeight) + .followedBy (transform)); } } diff --git a/src/gui/graphics/geometry/juce_Rectangle.h b/src/gui/graphics/geometry/juce_Rectangle.h index 4ddfcb9b56..a18630e937 100644 --- a/src/gui/graphics/geometry/juce_Rectangle.h +++ b/src/gui/graphics/geometry/juce_Rectangle.h @@ -703,7 +703,7 @@ public: const String toString() const { String s; - s.preallocateStorage (16); + s.preallocateBytes (32); s << x << ' ' << y << ' ' << w << ' ' << h; return s; } diff --git a/src/io/files/juce_File.cpp b/src/io/files/juce_File.cpp index 000b43e642..ea8ced65a8 100644 --- a/src/io/files/juce_File.cpp +++ b/src/io/files/juce_File.cpp @@ -868,19 +868,24 @@ const String File::getRelativePathFrom (const File& dir) const const int len = jmin (thisPath.length(), dirPath.length()); int commonBitLength = 0; - for (int i = 0; i < len; ++i) { -#if NAMES_ARE_CASE_SENSITIVE - if (thisPath[i] != dirPath[i]) -#else - if (CharacterFunctions::toLowerCase (thisPath[i]) - != CharacterFunctions::toLowerCase (dirPath[i])) -#endif + String::CharPointerType thisPathIter (thisPath.getCharPointer()); + String::CharPointerType dirPathIter (dirPath.getCharPointer()); + + for (int i = 0; i < len; ++i) { - break; - } + const juce_wchar c1 = thisPathIter.getAndAdvance(); + const juce_wchar c2 = dirPathIter.getAndAdvance(); - ++commonBitLength; + #if NAMES_ARE_CASE_SENSITIVE + if (c1 != c2) + #else + if (c1 != c2 && CharacterFunctions::toLowerCase (c1) != CharacterFunctions::toLowerCase (c2)) + #endif + break; + + ++commonBitLength; + } } while (commonBitLength > 0 && thisPath [commonBitLength - 1] != File::separator) @@ -1002,6 +1007,9 @@ public: expect (tempFile.hasFileExtension (".txt")); expect (tempFile.hasFileExtension ("txt")); expect (tempFile.withFileExtension ("xyz").hasFileExtension (".xyz")); + expect (tempFile.withFileExtension ("xyz").hasFileExtension ("abc;xyz;foo")); + expect (tempFile.withFileExtension ("xyz").hasFileExtension ("xyz;foo")); + expect (! tempFile.withFileExtension ("h").hasFileExtension ("bar;foo;xx")); expect (tempFile.getSiblingFile ("foo").isAChildOf (temp)); expect (tempFile.hasWriteAccess()); @@ -1013,7 +1021,7 @@ public: expect (tempFile.exists()); expect (tempFile.getSize() == 10); expect (std::abs ((int) (tempFile.getLastModificationTime().toMilliseconds() - Time::getCurrentTime().toMilliseconds())) < 3000); - expect (tempFile.loadFileAsString() == "0123456789"); + expectEquals (tempFile.loadFileAsString(), String ("0123456789")); expect (! demoFolder.containsSubDirectories()); expectEquals (tempFile.getRelativePathFrom (demoFolder.getParentDirectory()), demoFolder.getFileName() + File::separatorString + tempFile.getFileName()); diff --git a/src/io/network/juce_MACAddress.cpp b/src/io/network/juce_MACAddress.cpp index ac6ce5d1d4..e6af2b54c8 100644 --- a/src/io/network/juce_MACAddress.cpp +++ b/src/io/network/juce_MACAddress.cpp @@ -56,7 +56,6 @@ MACAddress::MACAddress (const uint8 bytes[6]) const String MACAddress::toString() const { String s; - s.preallocateStorage (18); for (int i = 0; i < numElementsInArray (asBytes); ++i) { diff --git a/src/io/streams/juce_MemoryOutputStream.cpp b/src/io/streams/juce_MemoryOutputStream.cpp index 26c99473af..4f9a3a30a4 100644 --- a/src/io/streams/juce_MemoryOutputStream.cpp +++ b/src/io/streams/juce_MemoryOutputStream.cpp @@ -131,7 +131,7 @@ int MemoryOutputStream::writeFromInputStream (InputStream& source, int64 maxNumB const String MemoryOutputStream::toUTF8() const { - return String::fromUTF8 (static_cast (getData()), getDataSize()); + return String::fromUTF8 (static_cast (getData()), (int) getDataSize()); } const String MemoryOutputStream::toString() const diff --git a/src/memory/juce_MemoryBlock.cpp b/src/memory/juce_MemoryBlock.cpp index d56de57281..89dae64431 100644 --- a/src/memory/juce_MemoryBlock.cpp +++ b/src/memory/juce_MemoryBlock.cpp @@ -284,7 +284,7 @@ void MemoryBlock::loadFromHexString (const String& hex) { ensureSize (hex.length() >> 1); char* dest = data; - int i = 0; + String::CharPointerType t (hex.getCharPointer()); for (;;) { @@ -296,7 +296,7 @@ void MemoryBlock::loadFromHexString (const String& hex) for (;;) { - const juce_wchar c = hex [i++]; + const juce_wchar c = t.getAndAdvance(); if (c >= '0' && c <= '9') { @@ -334,7 +334,7 @@ const String MemoryBlock::toBase64Encoding() const String destString ((unsigned int) size); // store the length, followed by a '.', and then the data. const int initialLen = destString.length(); - destString.preallocateStorage (initialLen + 2 + numChars); + destString.preallocateBytes (sizeof (String::CharPointerType::CharType) * (initialLen + 2 + numChars)); String::CharPointerType d (destString.getCharPointer()); d += initialLen; diff --git a/src/native/linux/juce_linux_Clipboard.cpp b/src/native/linux/juce_linux_Clipboard.cpp index 5294385686..d3c69cf023 100644 --- a/src/native/linux/juce_linux_Clipboard.cpp +++ b/src/native/linux/juce_linux_Clipboard.cpp @@ -146,15 +146,7 @@ void juce_handleSelectionRequest (XSelectionRequestEvent &evt) if (evt.selection == XA_PRIMARY || evt.selection == ClipboardHelpers::atom_CLIPBOARD) { - if (evt.target == XA_STRING) - { - // format data according to system locale - numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsCString() + 1; - data.calloc (numDataItems + 1); - ClipboardHelpers::localClipboardContent.copyToCString (data, numDataItems); - propertyFormat = 8; // bits/item - } - else if (evt.target == ClipboardHelpers::atom_UTF8_STRING) + if (evt.target == XA_STRING || evt.target == ClipboardHelpers::atom_UTF8_STRING) { // translate to utf8 numDataItems = ClipboardHelpers::localClipboardContent.getNumBytesAsUTF8() + 1; diff --git a/src/native/linux/juce_linux_Messaging.cpp b/src/native/linux/juce_linux_Messaging.cpp index 936f9d4049..88ed412110 100644 --- a/src/native/linux/juce_linux_Messaging.cpp +++ b/src/native/linux/juce_linux_Messaging.cpp @@ -268,7 +268,7 @@ namespace LinuxErrorHandling char requestStr[64] = { 0 }; XGetErrorText (display, event->error_code, errorStr, 64); - XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toCString(), "Unknown", requestStr, 64); + XGetErrorDatabaseText (display, "XRequest", String (event->request_code).toUTF8(), "Unknown", requestStr, 64); DBG ("ERROR: X returned " + String (errorStr) + " for operation " + String (requestStr)); #endif @@ -345,7 +345,7 @@ void MessageManager::doPlatformSpecificInitialisation() if (displayName.isEmpty()) displayName = ":0.0"; - display = XOpenDisplay (displayName.toCString()); + display = XOpenDisplay (displayName.toUTF8()); if (display != 0) // This is not fatal! we can run headless. { diff --git a/src/native/linux/juce_linux_Midi.cpp b/src/native/linux/juce_linux_Midi.cpp index d8c2766ac8..0f2414380e 100644 --- a/src/native/linux/juce_linux_Midi.cpp +++ b/src/native/linux/juce_linux_Midi.cpp @@ -128,7 +128,7 @@ namespace : SND_SEQ_OPEN_OUTPUT, 0) == 0) { snd_seq_set_client_name (seqHandle, - (deviceNameToOpen + (forInput ? " Input" : " Output")).toCString()); + (deviceNameToOpen + (forInput ? " Input" : " Output")).toUTF8()); const int portId = snd_seq_create_simple_port (seqHandle, diff --git a/src/native/linux/juce_linux_Threads.cpp b/src/native/linux/juce_linux_Threads.cpp index 7d4a3d5e2b..e8cf807eed 100644 --- a/src/native/linux/juce_linux_Threads.cpp +++ b/src/native/linux/juce_linux_Threads.cpp @@ -125,7 +125,7 @@ void PlatformUtilities::freeDynamicLibrary (void* handle) void* PlatformUtilities::getProcedureEntryPoint (void* libraryHandle, const String& procedureName) { - return dlsym (libraryHandle, procedureName.toCString()); + return dlsym (libraryHandle, procedureName.toUTF8()); } #endif diff --git a/src/native/mac/juce_mac_Strings.mm b/src/native/mac/juce_mac_Strings.mm index e16ff0600b..cec2447f1b 100644 --- a/src/native/mac/juce_mac_Strings.mm +++ b/src/native/mac/juce_mac_Strings.mm @@ -39,44 +39,26 @@ namespace { return [NSString stringWithUTF8String: s.toUTF8()]; } - - //============================================================================== - const String convertUTF16ToString (const UniChar* utf16) - { - String s; - - while (*utf16 != 0) - s += (juce_wchar) *utf16++; - - return s; - } } const String PlatformUtilities::cfStringToJuceString (CFStringRef cfString) { - String result; + if (cfString == 0) + return String::empty; - if (cfString != 0) - { - CFRange range = { 0, CFStringGetLength (cfString) }; - HeapBlock u (range.length + 1); - CFStringGetCharacters (cfString, range, u); - u[range.length] = 0; - result = convertUTF16ToString (u); - } + CFRange range = { 0, CFStringGetLength (cfString) }; + HeapBlock u (range.length + 1); + CFStringGetCharacters (cfString, range, u); + u[range.length] = 0; - return result; + return String (CharPointer_UTF16 ((const CharPointer_UTF16::CharType*) u.getData())); } CFStringRef PlatformUtilities::juceStringToCFString (const String& s) { - const int len = s.length(); - HeapBlock temp (len + 2); - - for (int i = 0; i <= len; ++i) - temp[i] = s[i]; + CharPointer_UTF16 utf16 (s.toUTF16()); - return CFStringCreateWithCharacters (kCFAllocatorDefault, temp, len); + return CFStringCreateWithCharacters (kCFAllocatorDefault, (const UniChar*) utf16.getAddress(), utf16.length()); } const String PlatformUtilities::convertToPrecomposedUnicode (const String& s) diff --git a/src/native/windows/juce_win32_Files.cpp b/src/native/windows/juce_win32_Files.cpp index 49a99c37b3..564614aa50 100644 --- a/src/native/windows/juce_win32_Files.cpp +++ b/src/native/windows/juce_win32_Files.cpp @@ -58,6 +58,8 @@ namespace WindowsFileHelpers const String getDriveFromPath (String path) { + // (mess with the string to make sure it's not sharing its internal storage) + path = (path + " ").dropLastCharacters(1); WCHAR* p = const_cast (path.toUTF16().getAddress()); if (PathStripToRoot (p)) diff --git a/src/native/windows/juce_win32_Network.cpp b/src/native/windows/juce_win32_Network.cpp index 6fbaaaccc6..f42eb949cf 100644 --- a/src/native/windows/juce_win32_Network.cpp +++ b/src/native/windows/juce_win32_Network.cpp @@ -459,8 +459,8 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd { MapiMessage message; zerostruct (message); - message.lpszSubject = (LPSTR) emailSubject.toCString(); - message.lpszNoteText = (LPSTR) bodyText.toCString(); + message.lpszSubject = (LPSTR) emailSubject.toUTF8().getAddress(); + message.lpszNoteText = (LPSTR) bodyText.toUTF8().getAddress(); MapiRecipDesc recip; zerostruct (recip); @@ -468,7 +468,7 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd String targetEmailAddress_ (targetEmailAddress); if (targetEmailAddress_.isEmpty()) targetEmailAddress_ = " "; // (Windows Mail can't deal with a blank address) - recip.lpszName = (LPSTR) targetEmailAddress_.toCString(); + recip.lpszName = (LPSTR) targetEmailAddress_.toUTF8().getAddress(); message.nRecipCount = 1; message.lpRecips = &recip; @@ -481,7 +481,7 @@ bool PlatformUtilities::launchEmailWithAttachments (const String& targetEmailAdd for (int i = 0; i < filesToAttach.size(); ++i) { files[i].nPosition = (ULONG) -1; - files[i].lpszPathName = (LPSTR) filesToAttach[i].toCString(); + files[i].lpszPathName = (LPSTR) filesToAttach[i].toUTF8().getAddress(); } ok = (mapiSendMail (0, 0, &message, MAPI_DIALOG | MAPI_LOGON_UI, 0) == SUCCESS_SUCCESS); diff --git a/src/native/windows/juce_win32_Threads.cpp b/src/native/windows/juce_win32_Threads.cpp index 633e83bd7a..305fe3ae29 100644 --- a/src/native/windows/juce_win32_Threads.cpp +++ b/src/native/windows/juce_win32_Threads.cpp @@ -166,7 +166,7 @@ void Thread::setCurrentThreadName (const String& name) } info; info.dwType = 0x1000; - info.szName = name.toCString(); + info.szName = name.toUTF8(); info.dwThreadID = GetCurrentThreadId(); info.dwFlags = 0; @@ -337,7 +337,7 @@ void PlatformUtilities::freeDynamicLibrary (void* h) void* PlatformUtilities::getProcedureEntryPoint (void* h, const String& name) { - return (h != 0) ? (void*) GetProcAddress ((HMODULE) h, name.toCString()) : 0; // (void* cast is required for mingw) + return (h != 0) ? (void*) GetProcAddress ((HMODULE) h, name.toUTF8()) : 0; // (void* cast is required for mingw) } @@ -345,10 +345,11 @@ void* PlatformUtilities::getProcedureEntryPoint (void* h, const String& name) class InterProcessLock::Pimpl { public: - Pimpl (const String& name, const int timeOutMillisecs) + Pimpl (String name, const int timeOutMillisecs) : handle (0), refCount (1) { - handle = CreateMutex (0, TRUE, ("Global\\" + name.replaceCharacter ('\\','/')).toUTF16()); + name = "Local\\" + name.replaceCharacter ('\\', '/'); + handle = CreateMutexW (0, TRUE, name.toUTF16().getAddress()); if (handle != 0 && GetLastError() == ERROR_ALREADY_EXISTS) { diff --git a/src/text/juce_CharPointer_UTF16.h b/src/text/juce_CharPointer_UTF16.h index 17d00d5556..3562aa3da2 100644 --- a/src/text/juce_CharPointer_UTF16.h +++ b/src/text/juce_CharPointer_UTF16.h @@ -103,6 +103,17 @@ public: return *this; } + /** Moves this pointer back to the previous character in the string. */ + CharPointer_UTF16& operator--() throw() + { + const juce_wchar n = *--data; + + if (n >= 0xdc00 && n <= 0xdfff) + --data; + + return *this; + } + /** Returns the character that this pointer is currently pointing to, and then advances the pointer to point to the next character. */ juce_wchar getAndAdvance() throw() @@ -126,10 +137,22 @@ public: /** Moves this pointer forwards by the specified number of characters. */ void operator+= (int numToSkip) throw() { - jassert (numToSkip >= 0); + if (numToSkip < 0) + { + while (++numToSkip <= 0) + --*this; + } + else + { + while (--numToSkip >= 0) + ++*this; + } + } - while (--numToSkip >= 0) - ++*this; + /** Moves this pointer backwards by the specified number of characters. */ + void operator-= (int numToSkip) throw() + { + operator+= (-numToSkip); } /** Returns the character at a given character index from the start of the string. */ @@ -148,6 +171,14 @@ public: return p; } + /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ + CharPointer_UTF16 operator- (const int numToSkip) const throw() + { + CharPointer_UTF16 p (*this); + p += -numToSkip; + return p; + } + /** Writes a unicode character to this string, and advances this pointer to point to the next position. */ void write (juce_wchar charToWrite) throw() { @@ -434,6 +465,12 @@ public: return true; } + /** Atomically swaps this pointer for a new value, returning the previous value. */ + CharPointer_UTF16 atomicSwap (const CharPointer_UTF16& newValue) + { + return CharPointer_UTF16 (reinterpret_cast &> (data).exchange (newValue.data)); + } + /** These values are the byte-order-mark (BOM) values for a UTF-16 stream. */ enum { diff --git a/src/text/juce_CharPointer_UTF8.h b/src/text/juce_CharPointer_UTF8.h index 3f10f1dc1e..b12da36100 100644 --- a/src/text/juce_CharPointer_UTF8.h +++ b/src/text/juce_CharPointer_UTF8.h @@ -131,6 +131,25 @@ public: return *this; } + /** Moves this pointer back to the previous character in the string. */ + CharPointer_UTF8& operator--() throw() + { + const char n = *--data; + + if ((n & 0xc0) == 0xc0) + { + int count = 3; + + do + { + --data; + } + while ((*data & 0xc0) == 0xc0 && --count >= 0); + } + + return *this; + } + /** Returns the character that this pointer is currently pointing to, and then advances the pointer to point to the next character. */ juce_wchar getAndAdvance() throw() @@ -179,10 +198,22 @@ public: /** Moves this pointer forwards by the specified number of characters. */ void operator+= (int numToSkip) throw() { - jassert (numToSkip >= 0); + if (numToSkip < 0) + { + while (++numToSkip <= 0) + --*this; + } + else + { + while (--numToSkip >= 0) + ++*this; + } + } - while (--numToSkip >= 0) - ++*this; + /** Moves this pointer backwards by the specified number of characters. */ + void operator-= (int numToSkip) throw() + { + operator+= (-numToSkip); } /** Returns the character at a given character index from the start of the string. */ @@ -201,6 +232,14 @@ public: return p; } + /** Returns a pointer which is moved backwards from this one by the specified number of characters. */ + CharPointer_UTF8 operator- (int numToSkip) const throw() + { + CharPointer_UTF8 p (*this); + p += -numToSkip; + return p; + } + /** Returns the number of characters in this string. */ size_t length() const throw() { @@ -520,6 +559,12 @@ public: return true; } + /** Atomically swaps this pointer for a new value, returning the previous value. */ + CharPointer_UTF8 atomicSwap (const CharPointer_UTF8& newValue) + { + return CharPointer_UTF8 (reinterpret_cast &> (data).exchange (newValue.data)); + } + /** These values are the byte-order-mark (BOM) values for a UTF-8 stream. */ enum { diff --git a/src/text/juce_CharacterFunctions.cpp b/src/text/juce_CharacterFunctions.cpp index 1ff37c63b7..0ae676eb3d 100644 --- a/src/text/juce_CharacterFunctions.cpp +++ b/src/text/juce_CharacterFunctions.cpp @@ -132,24 +132,6 @@ int CharacterFunctions::getHexDigitValue (const juce_wchar digit) throw() return -1; } -int CharacterFunctions::ftime (char* const dest, const int maxChars, const char* const format, const struct tm* const tm) throw() -{ - return (int) strftime (dest, maxChars, format, tm); -} - -int CharacterFunctions::ftime (juce_wchar* const dest, const int maxChars, const juce_wchar* const format, const struct tm* const tm) throw() -{ - #if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID - return (int) wcsftime (dest, maxChars, format, tm); - #else - HeapBlock tempDest; - tempDest.calloc (maxChars + 2); - int result = ftime (tempDest.getData(), maxChars, String (format).toUTF8(), tm); - CharPointer_UTF32 (dest).writeAll (CharPointer_UTF8 (tempDest.getData())); - return result; - #endif -} - #if JUCE_MSVC #pragma warning (pop) #endif diff --git a/src/text/juce_CharacterFunctions.h b/src/text/juce_CharacterFunctions.h index d036be532a..8e8469f79e 100644 --- a/src/text/juce_CharacterFunctions.h +++ b/src/text/juce_CharacterFunctions.h @@ -266,10 +266,6 @@ public: return isNeg ? -v : v; } - //============================================================================== - static int ftime (char* dest, int maxChars, const char* format, const struct tm* tm) throw(); - static int ftime (juce_wchar* dest, int maxChars, const juce_wchar* format, const struct tm* tm) throw(); - //============================================================================== template static size_t lengthUpTo (CharPointerType text, const size_t maxCharsToCount) throw() @@ -441,6 +437,24 @@ public: } } + template + static int indexOfIgnoreCase (CharPointerType1 haystack, const CharPointerType2& needle) throw() + { + int index = 0; + const int needleLength = (int) needle.length(); + + for (;;) + { + if (haystack.compareIgnoreCaseUpTo (needle, needleLength) == 0) + return index; + + if (haystack.getAndAdvance() == 0) + return -1; + + ++index; + } + } + template static int indexOfChar (Type text, const juce_wchar charToFind) throw() { diff --git a/src/text/juce_Identifier.h b/src/text/juce_Identifier.h index bf2c3a0343..9754fdabb8 100644 --- a/src/text/juce_Identifier.h +++ b/src/text/juce_Identifier.h @@ -76,11 +76,11 @@ public: const String toString() const { return name; } /** Returns this identifier's raw string pointer. */ - operator const juce_wchar*() const throw() { return name; } + operator const String::CharPointerType() const throw() { return name; } private: //============================================================================== - const juce_wchar* name; + String::CharPointerType name; static StringPool& getPool(); }; diff --git a/src/text/juce_LocalisedStrings.cpp b/src/text/juce_LocalisedStrings.cpp index 7db328f8c4..d677d3f987 100644 --- a/src/text/juce_LocalisedStrings.cpp +++ b/src/text/juce_LocalisedStrings.cpp @@ -59,10 +59,11 @@ namespace int findCloseQuote (const String& text, int startPos) { juce_wchar lastChar = 0; + String::CharPointerType t (text.getCharPointer() + startPos); for (;;) { - const juce_wchar c = text [startPos]; + const juce_wchar c = t.getAndAdvance(); if (c == 0 || (c == '"' && lastChar != '\\')) break; diff --git a/src/text/juce_String.cpp b/src/text/juce_String.cpp index 16004ac118..3b5a6f930d 100644 --- a/src/text/juce_String.cpp +++ b/src/text/juce_String.cpp @@ -57,20 +57,21 @@ class StringHolder { public: StringHolder() - : refCount (0x3fffffff), allocatedNumChars (0) + : refCount (0x3fffffff), allocatedNumBytes (sizeof (*text)) { text[0] = 0; } typedef String::CharPointerType CharPointerType; + typedef String::CharPointerType::CharType CharType; //============================================================================== - static const CharPointerType createUninitialised (const size_t numChars) + static const CharPointerType createUninitialisedBytes (const size_t numBytes) { - StringHolder* const s = reinterpret_cast (new char [sizeof (StringHolder) + numChars * sizeof (juce_wchar)]); + StringHolder* const s = reinterpret_cast (new char [sizeof (StringHolder) - sizeof (CharType) + numBytes]); s->refCount.value = 0; - s->allocatedNumChars = numChars; - return CharPointerType (&(s->text[0])); + s->allocatedNumBytes = numBytes; + return CharPointerType (s->text); } template @@ -79,8 +80,13 @@ public: if (text.getAddress() == 0 || text.isEmpty()) return getEmpty(); - const size_t numChars = text.length(); - const CharPointerType dest (createUninitialised (numChars)); + CharPointer t (text); + size_t bytesNeeded = sizeof (CharType); + + while (! t.isEmpty()) + bytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); + + const CharPointerType dest (createUninitialisedBytes (bytesNeeded)); CharPointerType (dest).writeAll (text); return dest; } @@ -88,15 +94,21 @@ public: template static const CharPointerType createFromCharPointer (const CharPointer& text, size_t maxChars) { - if (text.getAddress() == 0 || text.isEmpty()) + if (text.getAddress() == 0 || text.isEmpty() || maxChars == 0) return getEmpty(); - size_t numChars = text.lengthUpTo (maxChars); - if (numChars == 0) - return getEmpty(); + CharPointer end (text); + size_t numChars = 0; + size_t bytesNeeded = sizeof (CharType); - const CharPointerType dest (createUninitialised (numChars)); - CharPointerType (dest).writeWithCharLimit (text, (int) (numChars + 1)); + while (numChars < maxChars && ! end.isEmpty()) + { + bytesNeeded += CharPointerType::getBytesRequiredFor (end.getAndAdvance()); + ++numChars; + } + + const CharPointerType dest (createUninitialisedBytes (bytesNeeded)); + CharPointerType (dest).writeWithCharLimit (text, (int) numChars + 1); return dest; } @@ -106,32 +118,31 @@ public: if (start.getAddress() == 0 || start.isEmpty()) return getEmpty(); - size_t numChars = start.lengthUpTo (end); - if (numChars == 0) - return getEmpty(); + CharPointer e (start); + int numChars = 0; + size_t bytesNeeded = sizeof (CharType); - const CharPointerType dest (createUninitialised (numChars)); - CharPointerType (dest).writeWithCharLimit (start, (int) (numChars + 1)); - return dest; - } + while (e < end && ! e.isEmpty()) + { + bytesNeeded += CharPointerType::getBytesRequiredFor (e.getAndAdvance()); + ++numChars; + } - static CharPointerType createFromFixedLength (const juce_wchar* const src, const size_t numChars) - { - CharPointerType dest (createUninitialised (numChars)); - copyChars (dest, CharPointerType (src), (int) numChars); + const CharPointerType dest (createUninitialisedBytes (bytesNeeded)); + CharPointerType (dest).writeWithCharLimit (start, numChars + 1); return dest; } static const CharPointerType createFromFixedLength (const char* const src, const size_t numChars) { - const CharPointerType dest (createUninitialised (numChars)); + const CharPointerType dest (createUninitialisedBytes (numChars * sizeof (CharType) + sizeof (CharType))); CharPointerType (dest).writeWithCharLimit (CharPointer_UTF8 (src), (int) (numChars + 1)); return dest; } static inline const CharPointerType getEmpty() throw() { - return CharPointerType (&(empty.text[0])); + return CharPointerType (empty.text); } //============================================================================== @@ -159,42 +170,36 @@ public: if (b->refCount.get() <= 0) return text; - CharPointerType newText (createFromFixedLength (text, b->allocatedNumChars)); + CharPointerType newText (createUninitialisedBytes (b->allocatedNumBytes)); + memcpy (newText.getAddress(), text.getAddress(), b->allocatedNumBytes); release (b); return newText; } - static CharPointerType makeUniqueWithSize (const CharPointerType& text, size_t numChars) + static CharPointerType makeUniqueWithByteSize (const CharPointerType& text, size_t numBytes) { StringHolder* const b = bufferFromText (text); - if (b->refCount.get() <= 0 && b->allocatedNumChars >= numChars) + if (b->refCount.get() <= 0 && b->allocatedNumBytes >= numBytes) return text; - CharPointerType newText (createUninitialised (jmax (b->allocatedNumChars, numChars))); - copyChars (newText, text, b->allocatedNumChars); + CharPointerType newText (createUninitialisedBytes (jmax (b->allocatedNumBytes, numBytes))); + memcpy (newText.getAddress(), text.getAddress(), b->allocatedNumBytes); release (b); return newText; } - static size_t getAllocatedNumChars (const CharPointerType& text) throw() - { - return bufferFromText (text)->allocatedNumChars; - } - - static void copyChars (CharPointerType dest, const CharPointerType& src, const size_t numChars) throw() + static size_t getAllocatedNumBytes (const CharPointerType& text) throw() { - jassert (src.getAddress() != 0 && dest.getAddress() != 0); - memcpy (dest.getAddress(), src.getAddress(), numChars * sizeof (juce_wchar)); - CharPointerType (dest + (int) numChars).writeNull(); + return bufferFromText (text)->allocatedNumBytes; } //============================================================================== Atomic refCount; - size_t allocatedNumChars; - juce_wchar text[1]; + size_t allocatedNumBytes; + CharType text[1]; static StringHolder empty; @@ -211,21 +216,23 @@ StringHolder StringHolder::empty; const String String::empty; //============================================================================== -void String::appendFixedLength (const juce_wchar* const newText, const int numExtraChars) +void String::appendFixedLength (const char* const newText, const int numExtraChars) { if (numExtraChars > 0) { - const int oldLen = length(); - const int newTotalLen = oldLen + numExtraChars; + const size_t byteOffsetOfNull = getByteOffsetOfEnd(); + const size_t newBytesNeeded = sizeof (CharPointerType::CharType) + byteOffsetOfNull + sizeof (CharPointerType::CharType) * numExtraChars; - text = StringHolder::makeUniqueWithSize (text, newTotalLen); - StringHolder::copyChars (text + oldLen, CharPointer_UTF32 (newText), numExtraChars); + text = StringHolder::makeUniqueWithByteSize (text, newBytesNeeded); + + CharPointerType newEnd (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)); + newEnd.writeWithCharLimit (CharPointer_ASCII (newText), numExtraChars); } } -void String::preallocateStorage (const size_t numChars) +void String::preallocateBytes (const size_t numBytesNeeded) { - text = StringHolder::makeUniqueWithSize (text, numChars); + text = StringHolder::makeUniqueWithByteSize (text, numBytesNeeded + sizeof (CharPointerType::CharType)); } //============================================================================== @@ -257,20 +264,20 @@ String& String::operator= (const String& other) throw() return *this; } -inline String::Preallocation::Preallocation (const size_t numChars_) : numChars (numChars_) {} +inline String::PreallocationBytes::PreallocationBytes (const size_t numBytes_) : numBytes (numBytes_) {} -String::String (const Preallocation& preallocationSize) - : text (StringHolder::createUninitialised (preallocationSize.numChars)) +String::String (const PreallocationBytes& preallocationSize) + : text (StringHolder::createUninitialisedBytes (preallocationSize.numBytes + sizeof (CharPointerType::CharType))) { } -String::String (const String& stringToCopy, const size_t charsToAllocate) +/*String::String (const String& stringToCopy, const size_t bytesToAllocate) : text (0) { - const size_t otherSize = StringHolder::getAllocatedNumChars (stringToCopy.text); - text = StringHolder::createUninitialised (jmax (charsToAllocate, otherSize)); + const size_t otherSize = StringHolder::getAllocatedNumBytes (stringToCopy.text); + text = StringHolder::createUninitialised (jmax (bytesToAllocate, otherSize)); StringHolder::copyChars (text, stringToCopy.text, otherSize); -} +}*/ String::String (const char* const t) : text (StringHolder::createFromCharPointer (CharPointer_ASCII (t))) @@ -325,11 +332,31 @@ String::String (const CharPointer_UTF8& t) { } +String::String (const CharPointer_UTF8& t, const size_t maxChars) + : text (StringHolder::createFromCharPointer (t, maxChars)) +{ +} + +String::String (const CharPointer_UTF8& start, const CharPointer_UTF8& end) + : text (StringHolder::createFromCharPointer (start, end)) +{ +} + String::String (const CharPointer_UTF16& t) : text (StringHolder::createFromCharPointer (t)) { } +String::String (const CharPointer_UTF16& t, const size_t maxChars) + : text (StringHolder::createFromCharPointer (t, maxChars)) +{ +} + +String::String (const CharPointer_UTF16& start, const CharPointer_UTF16& end) + : text (StringHolder::createFromCharPointer (start, end)) +{ +} + String::String (const CharPointer_UTF32& t) : text (StringHolder::createFromCharPointer (t)) { @@ -366,7 +393,7 @@ String::String (const wchar_t* const t, size_t maxChars) const String String::charToString (const juce_wchar character) { - String result (Preallocation (1)); + String result (PreallocationBytes (sizeof (CharPointerType::CharType))); CharPointerType t (result.text); t.write (character); t.writeNull(); @@ -377,14 +404,14 @@ const String String::charToString (const juce_wchar character) namespace NumberToStringConverters { // pass in a pointer to the END of a buffer.. - juce_wchar* numberToString (juce_wchar* t, const int64 n) throw() + char* numberToString (char* t, const int64 n) throw() { *--t = 0; int64 v = (n >= 0) ? n : -n; do { - *--t = (juce_wchar) ('0' + (int) (v % 10)); + *--t = (char) ('0' + (int) (v % 10)); v /= 10; } while (v > 0); @@ -395,13 +422,13 @@ namespace NumberToStringConverters return t; } - juce_wchar* numberToString (juce_wchar* t, uint64 v) throw() + char* numberToString (char* t, uint64 v) throw() { *--t = 0; do { - *--t = (juce_wchar) ('0' + (int) (v % 10)); + *--t = (char) ('0' + (int) (v % 10)); v /= 10; } while (v > 0); @@ -409,7 +436,7 @@ namespace NumberToStringConverters return t; } - juce_wchar* numberToString (juce_wchar* t, const int n) throw() + char* numberToString (char* t, const int n) throw() { if (n == (int) 0x80000000) // (would cause an overflow) return numberToString (t, (int64) n); @@ -419,7 +446,7 @@ namespace NumberToStringConverters do { - *--t = (juce_wchar) ('0' + (v % 10)); + *--t = (char) ('0' + (v % 10)); v /= 10; } while (v > 0); @@ -430,13 +457,13 @@ namespace NumberToStringConverters return t; } - juce_wchar* numberToString (juce_wchar* t, unsigned int v) throw() + char* numberToString (char* t, unsigned int v) throw() { *--t = 0; do { - *--t = (juce_wchar) ('0' + (v % 10)); + *--t = (char) ('0' + (v % 10)); v /= 10; } while (v > 0); @@ -444,12 +471,12 @@ namespace NumberToStringConverters return t; } - juce_wchar getDecimalPoint() + char getDecimalPoint() { #if JUCE_VC7_OR_EARLIER - static juce_wchar dp = std::_USE (std::locale(), std::numpunct ).decimal_point(); + static char dp = (char) std::_USE (std::locale(), std::numpunct ).decimal_point(); #else - static juce_wchar dp = std::use_facet > (std::locale()).decimal_point(); + static char dp = (char) std::use_facet > (std::locale()).decimal_point(); #endif return dp; } @@ -490,9 +517,9 @@ namespace NumberToStringConverters template const String::CharPointerType createFromInteger (const IntegerType number) { - juce_wchar buffer [32]; - juce_wchar* const end = buffer + numElementsInArray (buffer); - juce_wchar* const start = numberToString (end, number); + char buffer [32]; + char* const end = buffer + numElementsInArray (buffer); + char* const start = numberToString (end, number); return StringHolder::createFromFixedLength (start, end - start - 1); } @@ -553,9 +580,14 @@ int String::length() const throw() return (int) text.length(); } +size_t String::getByteOffsetOfEnd() const throw() +{ + return ((char*) text.findTerminatingNull().getAddress()) - (char*) text.getAddress(); +} + const juce_wchar String::operator[] (int index) const throw() { - jassert (index == 0 || isPositiveAndNotGreaterThan (index, length())); + jassert (index == 0 || (index > 0 && index <= (int) text.lengthUpTo (index + 1))); return text [index]; } @@ -761,9 +793,9 @@ String& String::operator+= (const wchar_t* t) String& String::operator+= (const int number) { - juce_wchar buffer [16]; - juce_wchar* const end = buffer + numElementsInArray (buffer); - juce_wchar* const start = NumberToStringConverters::numberToString (end, number); + char buffer [16]; + char* const end = buffer + numElementsInArray (buffer); + char* const start = NumberToStringConverters::numberToString (end, number); appendFixedLength (start, (int) (end - start)); return *this; } @@ -907,87 +939,108 @@ int String::indexOfChar (const juce_wchar character) const throw() return text.indexOf (character); } -int String::lastIndexOfChar (const juce_wchar character) const throw() +int String::indexOfChar (const int startIndex, const juce_wchar character) const throw() { - for (int i = length(); --i >= 0;) - if (text[i] == character) - return i; + CharPointerType t (text); + + for (int i = 0; ! t.isEmpty(); ++i) + { + if (i >= startIndex) + { + if (t.getAndAdvance() == character) + return i; + } + else + { + ++t; + } + } return -1; } -int String::indexOf (const String& t) const throw() +int String::lastIndexOfChar (const juce_wchar character) const throw() { - return t.isEmpty() ? 0 : text.indexOf (t.text); -} + CharPointerType t (text); + int last = -1; -int String::indexOfChar (const int startIndex, - const juce_wchar character) const throw() -{ - const int i = (text + startIndex).indexOf (character); - return i < 0 ? -1 : (i + startIndex); + for (int i = 0; ! t.isEmpty(); ++i) + if (t.getAndAdvance() == character) + last = i; + + return last; } -int String::indexOfAnyOf (const String& charactersToLookFor, - const int startIndex, - const bool ignoreCase) const throw() +int String::indexOfAnyOf (const String& charactersToLookFor, const int startIndex, const bool ignoreCase) const throw() { - if (startIndex > 0 && startIndex >= length()) - return -1; - CharPointerType t (text); - int i = jmax (0, startIndex); - t += i; - while (! t.isEmpty()) + for (int i = 0; ! t.isEmpty(); ++i) { - if (charactersToLookFor.text.indexOf (*t, ignoreCase) >= 0) - return i; - - ++i; - ++t; + if (i >= startIndex) + { + if (charactersToLookFor.text.indexOf (t.getAndAdvance(), ignoreCase) >= 0) + return i; + } + else + { + ++t; + } } return -1; } -int String::indexOf (const int startIndex, const String& other) const throw() +int String::indexOf (const String& other) const throw() { - if (startIndex > 0 && startIndex >= length()) - return -1; - - int i = CharPointerType (text + jmax (0, startIndex)).indexOf (other.text); - return i >= 0 ? i + startIndex : -1; + return other.isEmpty() ? 0 : text.indexOf (other.text); } int String::indexOfIgnoreCase (const String& other) const throw() +{ + return other.isEmpty() ? 0 : CharacterFunctions::indexOfIgnoreCase (text, other.text); +} + +int String::indexOf (const int startIndex, const String& other) const throw() { if (other.isEmpty()) - return 0; + return -1; - const int len = other.length(); - const int end = length() - len; + CharPointerType t (text); - for (int i = 0; i <= end; ++i) - if (CharPointerType (text + i).compareIgnoreCaseUpTo (other.text, len) == 0) - return i; + for (int i = startIndex; --i >= 0;) + { + if (t.isEmpty()) + return -1; - return -1; + ++t; + } + + int found = t.indexOf (other.text); + if (found >= 0) + found += startIndex; + return found; } int String::indexOfIgnoreCase (const int startIndex, const String& other) const throw() { - if (other.isNotEmpty()) + if (other.isEmpty()) + return -1; + + CharPointerType t (text); + + for (int i = startIndex; --i >= 0;) { - const int len = other.length(); - const int end = length() - len; + if (t.isEmpty()) + return -1; - for (int i = jmax (0, startIndex); i <= end; ++i) - if (CharPointerType (text + i).compareIgnoreCaseUpTo (other.text, len) == 0) - return i; + ++t; } - return -1; + int found = CharacterFunctions::indexOfIgnoreCase (t, other.text); + if (found >= 0) + found += startIndex; + return found; } int String::lastIndexOf (const String& other) const throw() @@ -1042,11 +1095,14 @@ int String::lastIndexOfIgnoreCase (const String& other) const throw() int String::lastIndexOfAnyOf (const String& charactersToLookFor, const bool ignoreCase) const throw() { - for (int i = length(); --i >= 0;) - if (charactersToLookFor.text.indexOf (text[i], ignoreCase) >= 0) - return i; + CharPointerType t (text); + int last = -1; - return -1; + for (int i = 0; ! t.isEmpty(); ++i) + if (charactersToLookFor.text.indexOf (t.getAndAdvance(), ignoreCase) >= 0) + last = i; + + return last; } bool String::contains (const String& other) const throw() @@ -1097,7 +1153,7 @@ int String::indexOfWholeWordIgnoreCase (const String& word) const throw() for (int i = 0; i <= end; ++i) { if (t.compareIgnoreCaseUpTo (word.text, wordLen) == 0 - && (i == 0 || ! (t + -1).isLetterOrDigit()) + && (i == 0 || ! (t - 1).isLetterOrDigit()) && ! (t + wordLen).isLetterOrDigit()) return i; @@ -1197,7 +1253,7 @@ const String String::repeatedString (const String& stringToRepeat, int numberOfT if (numberOfTimesToRepeat <= 0) return String::empty; - String result (Preallocation (stringToRepeat.length() * numberOfTimesToRepeat + 1)); + String result (PreallocationBytes (stringToRepeat.getByteOffsetOfEnd() * numberOfTimesToRepeat)); CharPointerType n (result.text); while (--numberOfTimesToRepeat >= 0) @@ -1210,18 +1266,26 @@ const String String::paddedLeft (const juce_wchar padCharacter, int minimumLengt { jassert (padCharacter != 0); - const int len = length(); - if (len >= minimumLength || padCharacter == 0) + int extraChars = minimumLength; + CharPointerType end (text); + + while (! end.isEmpty()) + { + --extraChars; + ++end; + } + + if (extraChars <= 0 || padCharacter == 0) return *this; - String result (Preallocation (minimumLength + 1)); + const size_t currentByteSize = ((char*) end.getAddress()) - (char*) text.getAddress(); + String result (PreallocationBytes (currentByteSize + extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); CharPointerType n (result.text); - minimumLength -= len; - while (--minimumLength >= 0) + while (--extraChars >= 0) n.write (padCharacter); - StringHolder::copyChars (n, text, len); + n.writeAll (text); return result; } @@ -1229,15 +1293,25 @@ const String String::paddedRight (const juce_wchar padCharacter, int minimumLeng { jassert (padCharacter != 0); - const int len = length(); - if (len >= minimumLength || padCharacter == 0) + int extraChars = minimumLength; + CharPointerType end (text); + + while (! end.isEmpty()) + { + --extraChars; + ++end; + } + + if (extraChars <= 0 || padCharacter == 0) return *this; - String result (*this, (size_t) minimumLength); - CharPointerType n (result.text + len); + const size_t currentByteSize = ((char*) end.getAddress()) - (char*) text.getAddress(); + String result (PreallocationBytes (currentByteSize + extraChars * CharPointerType::getBytesRequiredFor (padCharacter))); + CharPointerType n (result.text); + + n.writeAll (text); - minimumLength -= len; - while (--minimumLength >= 0) + while (--extraChars >= 0) n.write (padCharacter); n.writeNull(); @@ -1261,39 +1335,52 @@ const String String::replaceSection (int index, int numCharsToReplace, const Str jassertfalse; } - const int len = length(); + int i = 0; + CharPointerType insertPoint (text); - if (index + numCharsToReplace > len) + while (i < index) { - if (index > len) + if (insertPoint.isEmpty()) { // replacing beyond the end of the string? - index = len; jassertfalse; + return *this + stringToInsert; } - numCharsToReplace = len - index; + ++insertPoint; + ++i; } - const int newStringLen = stringToInsert.length(); - const int newTotalLen = len + newStringLen - numCharsToReplace; + CharPointerType startOfRemainder (insertPoint); - if (newTotalLen <= 0) - return String::empty; + i = 0; + while (i < numCharsToReplace && ! startOfRemainder.isEmpty()) + { + ++startOfRemainder; + ++i; + } - String result (Preallocation ((size_t) newTotalLen)); + if (insertPoint == text && startOfRemainder.isEmpty()) + return stringToInsert; - StringHolder::copyChars (result.text, text, index); + const size_t initialBytes = ((char*) insertPoint.getAddress()) - (char*) text.getAddress(); + const size_t newStringBytes = stringToInsert.getByteOffsetOfEnd(); + const size_t remainderBytes = ((char*) startOfRemainder.findTerminatingNull().getAddress()) - (char*) startOfRemainder.getAddress(); - if (newStringLen > 0) - StringHolder::copyChars (result.text + index, stringToInsert.text, newStringLen); + const size_t newTotalBytes = initialBytes + newStringBytes + remainderBytes; + if (newTotalBytes <= 0) + return String::empty; - const int endStringLen = newTotalLen - (index + newStringLen); + String result (PreallocationBytes ((size_t) newTotalBytes)); - if (endStringLen > 0) - StringHolder::copyChars (result.text + (index + newStringLen), - text + (index + numCharsToReplace), - endStringLen); + char* dest = (char*) result.text.getAddress(); + memcpy (dest, text.getAddress(), initialBytes); + dest += initialBytes; + memcpy (dest, stringToInsert.text.getAddress(), newStringBytes); + dest += newStringBytes; + memcpy (dest, startOfRemainder.getAddress(), remainderBytes); + dest += remainderBytes; + CharPointerType ((CharPointerType::CharType*) dest).writeNull(); return result; } @@ -1316,48 +1403,89 @@ const String String::replace (const String& stringToReplace, const String& strin return result; } -const String String::replaceCharacter (const juce_wchar charToReplace, const juce_wchar charToInsert) const +class StringCreationHelper { - const int index = indexOfChar (charToReplace); +public: + StringCreationHelper (const size_t initialBytes) + : source (0), dest (0), allocatedBytes (initialBytes), bytesWritten (0) + { + result.preallocateBytes (allocatedBytes); + dest = result.getCharPointer(); + } - if (index < 0) + StringCreationHelper (const String::CharPointerType& source_) + : source (source_), dest (0), allocatedBytes (StringHolder::getAllocatedNumBytes (source)), bytesWritten (0) + { + result.preallocateBytes (allocatedBytes); + dest = result.getCharPointer(); + } + + void write (juce_wchar c) + { + bytesWritten += String::CharPointerType::getBytesRequiredFor (c); + + if (bytesWritten > allocatedBytes) + { + allocatedBytes += jmax ((size_t) 8, allocatedBytes / 16); + const size_t destOffset = ((char*) dest.getAddress()) - (char*) result.getCharPointer().getAddress(); + result.preallocateBytes (allocatedBytes); + dest = addBytesToPointer (result.getCharPointer().getAddress(), (int) destOffset); + } + + dest.write (c); + } + + String result; + String::CharPointerType source; + +private: + String::CharPointerType dest; + size_t allocatedBytes, bytesWritten; +}; + +const String String::replaceCharacter (const juce_wchar charToReplace, const juce_wchar charToInsert) const +{ + if (! containsChar (charToReplace)) return *this; - String result (*this, size_t()); - CharPointerType t (result.text + index); + StringCreationHelper builder (text); - while (! t.isEmpty()) + for (;;) { - if (*t == charToReplace) - t.replaceChar (charToInsert); + juce_wchar c = builder.source.getAndAdvance(); - ++t; + if (c == charToReplace) + c = charToInsert; + + builder.write (c); + + if (c == 0) + break; } - return result; + return builder.result; } const String String::replaceCharacters (const String& charactersToReplace, const String& charactersToInsertInstead) const { - String result (*this, size_t()); - CharPointerType t (result.text); - const int len2 = charactersToInsertInstead.length(); - - // the two strings passed in are supposed to be the same length! - jassert (len2 == charactersToReplace.length()); + StringCreationHelper builder (text); - while (! t.isEmpty()) + for (;;) { - const int index = charactersToReplace.indexOfChar (*t); + juce_wchar c = builder.source.getAndAdvance(); - if (isPositiveAndBelow (index, len2)) - t.replaceChar (charactersToInsertInstead [index]); + const int index = charactersToReplace.indexOfChar (c); + if (index >= 0) + c = charactersToInsertInstead [index]; - ++t; + builder.write (c); + + if (c == 0) + break; } - return result; + return builder.result; } //============================================================================== @@ -1375,76 +1503,87 @@ bool String::startsWithChar (const juce_wchar character) const throw() { jassert (character != 0); // strings can't contain a null character! - return text[0] == character; + return *text == character; } bool String::endsWithChar (const juce_wchar character) const throw() { jassert (character != 0); // strings can't contain a null character! - return text[0] != 0 - && text [length() - 1] == character; + if (text.isEmpty()) + return false; + + CharPointerType t (text.findTerminatingNull()); + return *--t == character; } bool String::endsWith (const String& other) const throw() { - const int thisLen = length(); - const int otherLen = other.length(); + CharPointerType end (text.findTerminatingNull()); + CharPointerType otherEnd (other.text.findTerminatingNull()); + + while (end > text && otherEnd > other.text) + { + --end; + --otherEnd; + + if (*end != *otherEnd) + return false; + } - return thisLen >= otherLen - && CharPointerType (text + thisLen - otherLen).compare (other.text) == 0; + return otherEnd == other.text; } bool String::endsWithIgnoreCase (const String& other) const throw() { - const int thisLen = length(); - const int otherLen = other.length(); + CharPointerType end (text.findTerminatingNull()); + CharPointerType otherEnd (other.text.findTerminatingNull()); + + while (end > text && otherEnd > other.text) + { + --end; + --otherEnd; - return thisLen >= otherLen - && CharPointerType (text + thisLen - otherLen).compareIgnoreCase (other.text) == 0; + if (end.toLowerCase() != otherEnd.toLowerCase()) + return false; + } + + return otherEnd == other.text; } //============================================================================== const String String::toUpperCase() const { - String result (Preallocation (this->length())); - - CharPointerType dest (result.text); - CharPointerType src (text); + StringCreationHelper builder (text); for (;;) { - const juce_wchar c = src.toUpperCase(); - dest.write (c); + juce_wchar c = builder.source.toUpperCase(); + ++(builder.source); + builder.write (c); if (c == 0) break; - - ++src; } - return result; + return builder.result; } const String String::toLowerCase() const { - String result (Preallocation (this->length())); - - CharPointerType dest (result.text); - CharPointerType src (text); + StringCreationHelper builder (text); for (;;) { - const juce_wchar c = src.toLowerCase(); - dest.write (c); + juce_wchar c = builder.source.toLowerCase(); + ++(builder.source); + builder.write (c); if (c == 0) break; - - ++src; } - return result; + return builder.result; } //============================================================================== @@ -1453,39 +1592,60 @@ juce_wchar String::getLastCharacter() const throw() return isEmpty() ? juce_wchar() : text [length() - 1]; } -const String String::substring (int start, int end) const +const String String::substring (int start, const int end) const { if (start < 0) start = 0; - else if (end <= start) + + if (end <= start) return empty; - int len = 0; - while (len <= end && text [len] != 0) - ++len; + int i = 0; + CharPointerType t1 (text); - if (end >= len) + while (i < start) { - if (start == 0) - return *this; + if (t1.isEmpty()) + return empty; - end = len; + ++i; + ++t1; } - return String (text + start, end - start); + CharPointerType t2 (t1); + while (i < end) + { + if (t2.isEmpty()) + { + if (start == 0) + return *this; + + break; + } + + ++i; + ++t2; + } + + return String (t1, t2); } -const String String::substring (const int start) const +const String String::substring (int start) const { if (start <= 0) return *this; - const int len = length(); + CharPointerType t (text); - if (start >= len) - return empty; + while (--start >= 0) + { + if (t.isEmpty()) + return empty; - return String (text + start, len - start); + ++t; + } + + return String (t); } const String String::dropLastCharacters (const int numberToDrop) const @@ -1683,23 +1843,21 @@ const String String::retainCharacters (const String& charactersToRetain) const if (isEmpty()) return empty; - String result (Preallocation (StringHolder::getAllocatedNumChars (text))); - CharPointerType dst (result.text); - CharPointerType src (text); + StringCreationHelper builder (text); for (;;) { - const juce_wchar c = src.getAndAdvance(); + juce_wchar c = builder.source.getAndAdvance(); + + if (charactersToRetain.containsChar (c)) + builder.write (c); if (c == 0) break; - - if (charactersToRetain.containsChar (c)) - dst.write (c); } - dst.writeNull(); - return result; + builder.write (0); + return builder.result; } const String String::removeCharacters (const String& charactersToRemove) const @@ -1707,23 +1865,20 @@ const String String::removeCharacters (const String& charactersToRemove) const if (isEmpty()) return empty; - String result (Preallocation (StringHolder::getAllocatedNumChars (text))); - CharPointerType dst (result.text); - CharPointerType src (text); + StringCreationHelper builder (text); for (;;) { - const juce_wchar c = src.getAndAdvance(); + juce_wchar c = builder.source.getAndAdvance(); + + if (! charactersToRemove.containsChar (c)) + builder.write (c); if (c == 0) break; - - if (! charactersToRemove.containsChar (c)) - dst.write (c); } - dst.writeNull(); - return result; + return builder.result; } const String String::initialSectionContainingOnly (const String& permittedCharacters) const @@ -1801,39 +1956,33 @@ const String String::formatted (const juce_wchar* const pf, ... ) va_start (args, pf); size_t bufferSize = 256; - String result (Preallocation ((size_t) bufferSize)); - result.text[0] = 0; for (;;) { #if JUCE_LINUX && JUCE_64BIT + HeapBlock temp (bufferSize); va_list tempArgs; va_copy (tempArgs, args); - const int num = (int) vswprintf (result.text.getAddress(), bufferSize - 1, pf, tempArgs); + const int num = (int) vswprintf (temp.getData(), bufferSize - 1, pf, tempArgs); va_end (tempArgs); #elif JUCE_WINDOWS HeapBlock temp (bufferSize); const int num = (int) _vsnwprintf (temp.getData(), bufferSize - 1, String (pf).toUTF16(), args); - if (num > 0) - CharPointerType (result.text).writeAll (CharPointer_UTF16 (temp.getData())); #elif JUCE_ANDROID HeapBlock temp (bufferSize); const int num = (int) vsnprintf (temp.getData(), bufferSize - 1, String (pf).toUTF8(), args); - if (num > 0) - CharPointerType (result.text).writeAll (CharPointer_UTF8 (temp.getData())); #else - const int num = (int) vswprintf (result.text.getAddress(), bufferSize - 1, pf, args); + HeapBlock temp (bufferSize); + const int num = (int) vswprintf (temp.getData(), bufferSize - 1, pf, args); #endif if (num > 0) - return result; + return String (temp); bufferSize += 256; if (num == 0 || bufferSize > 65536) // the upper limit is a sanity check to avoid situations where vprintf repeatedly break; // returns -1 because of an error rather than because it needs more space. - - result.preallocateStorage (bufferSize); } return empty; @@ -1945,7 +2094,7 @@ const String String::toHexString (const unsigned char* data, const int size, con if (groupSize > 0) numChars += size / groupSize; - String s (Preallocation ((size_t) numChars)); + String s (PreallocationBytes (sizeof (CharPointerType::CharType) * (size_t) numChars)); CharPointerType dest (s.text); @@ -1994,25 +2143,23 @@ const String String::createStringFromData (const void* const data_, const int si const bool bigEndian = (data[0] == (uint8) CharPointer_UTF16::byteOrderMarkBE1); const int numChars = size / 2 - 1; - String result; - result.preallocateStorage (numChars + 2); + StringCreationHelper builder (numChars); const uint16* const src = (const uint16*) (data + 2); - CharPointerType dst (result.getCharPointer()); if (bigEndian) { for (int i = 0; i < numChars; ++i) - dst.write ((juce_wchar) ByteOrder::swapIfLittleEndian (src[i])); + builder.write ((juce_wchar) ByteOrder::swapIfLittleEndian (src[i])); } else { for (int i = 0; i < numChars; ++i) - dst.write ((juce_wchar) ByteOrder::swapIfBigEndian (src[i])); + builder.write ((juce_wchar) ByteOrder::swapIfBigEndian (src[i])); } - dst.writeNull(); - return result; + builder.write (0); + return builder.result; } else { @@ -2027,68 +2174,104 @@ const String String::createStringFromData (const void* const data_, const int si } //============================================================================== -void* String::createSpaceAtEndOfBuffer (const size_t numExtraBytes) const +static juce_wchar emptyChar = 0; + +template +struct StringEncodingConverter { - const int currentLen = length() + 1; + static const CharPointerType_Dest convert (const String& s) + { + String& source = const_cast (s); - String& mutableThis = const_cast (*this); - mutableThis.preallocateStorage (currentLen + 1 + numExtraBytes / sizeof (juce_wchar)); + typedef typename CharPointerType_Dest::CharType DestChar; - return (mutableThis.text + currentLen).getAddress(); -} + if (source.isEmpty()) + return CharPointerType_Dest (reinterpret_cast (&emptyChar)); -const CharPointer_UTF8 String::toUTF8() const + CharPointerType_Src text (source.getCharPointer()); + const size_t extraBytesNeeded = CharPointerType_Dest::getBytesRequiredFor (text); + const size_t endOffset = text.sizeInBytes(); + + source.preallocateBytes (endOffset + extraBytesNeeded); + text = source.getCharPointer(); + + void* newSpace = addBytesToPointer (text.getAddress(), (int) endOffset); + const CharPointerType_Dest extraSpace (static_cast (newSpace)); + + #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) + const int bytesToClear = jmin ((int) extraBytesNeeded, 4); + zeromem (addBytesToPointer (newSpace, (int) (extraBytesNeeded - bytesToClear)), bytesToClear); + #endif + + CharPointerType_Dest (extraSpace).writeAll (text); + return extraSpace; + } +}; + +template <> +struct StringEncodingConverter { - if (isEmpty()) - return CharPointer_UTF8 (reinterpret_cast (text.getAddress())); + static const CharPointer_UTF8 convert (const String& source) throw() { return CharPointer_UTF8 ((CharPointer_UTF8::CharType*) source.getCharPointer().getAddress()); } +}; - const size_t extraBytesNeeded = CharPointer_UTF8::getBytesRequiredFor (text); - CharPointer_UTF8 extraSpace (static_cast (createSpaceAtEndOfBuffer (extraBytesNeeded))); +template <> +struct StringEncodingConverter +{ + static const CharPointer_UTF16 convert (const String& source) throw() { return CharPointer_UTF16 ((CharPointer_UTF16::CharType*) source.getCharPointer().getAddress()); } +}; - #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) - *(juce_wchar*) (addBytesToPointer (extraSpace.getAddress(), (extraBytesNeeded & ~(sizeof (juce_wchar) - 1)))) = 0; - #endif +template <> +struct StringEncodingConverter +{ + static const CharPointer_UTF32 convert (const String& source) throw() { return CharPointer_UTF32 ((CharPointer_UTF32::CharType*) source.getCharPointer().getAddress()); } +}; - CharPointer_UTF8 (extraSpace).writeAll (text); - return extraSpace; +const CharPointer_UTF8 String::toUTF8() const +{ + return StringEncodingConverter ::convert (*this); } CharPointer_UTF16 String::toUTF16() const { - if (isEmpty()) - return CharPointer_UTF16 (reinterpret_cast (text.getAddress())); - - const size_t extraBytesNeeded = CharPointer_UTF16::getBytesRequiredFor (text); - CharPointer_UTF16 extraSpace (static_cast (createSpaceAtEndOfBuffer (extraBytesNeeded))); - - #if JUCE_DEBUG // (This just avoids spurious warnings from valgrind about the uninitialised bytes at the end of the buffer..) - *(juce_wchar*) (addBytesToPointer (extraSpace.getAddress(), (extraBytesNeeded & ~(sizeof (juce_wchar) - 1)))) = 0; - #endif + return StringEncodingConverter ::convert (*this); +} - CharPointer_UTF16 (extraSpace).writeAll (text); - return extraSpace; +CharPointer_UTF32 String::toUTF32() const +{ + return StringEncodingConverter ::convert (*this); } -int String::copyToUTF8 (CharPointer_UTF8::CharType* const buffer, const int maxBufferSizeBytes) const throw() +//============================================================================== +template +struct StringCopier { - jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! + static int copyToBuffer (const CharPointerType_Src& source, typename CharPointerType_Dest::CharType* const buffer, const int maxBufferSizeBytes) + { + jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! - if (buffer == 0) - return (int) CharPointer_UTF8::getBytesRequiredFor (text); + if (buffer == 0) + return (int) CharPointerType_Dest::getBytesRequiredFor (source); + + return CharPointerType_Dest (buffer).writeWithDestByteLimit (source, maxBufferSizeBytes); + } +}; - return CharPointer_UTF8 (buffer).writeWithDestByteLimit (text, maxBufferSizeBytes); +int String::copyToUTF8 (CharPointer_UTF8::CharType* const buffer, const int maxBufferSizeBytes) const throw() +{ + return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); } int String::copyToUTF16 (CharPointer_UTF16::CharType* const buffer, int maxBufferSizeBytes) const throw() { - jassert (maxBufferSizeBytes >= 0); // keep this value positive, or no characters will be copied! - - if (buffer == 0) - return (int) CharPointer_UTF16::getBytesRequiredFor (text); + return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); +} - return CharPointer_UTF16 (buffer).writeWithDestByteLimit (text, maxBufferSizeBytes); +int String::copyToUTF32 (CharPointer_UTF32::CharType* const buffer, int maxBufferSizeBytes) const throw() +{ + return StringCopier ::copyToBuffer (text, buffer, maxBufferSizeBytes); } +//============================================================================== int String::getNumBytesAsUTF8() const throw() { return (int) CharPointer_UTF8::getBytesRequiredFor (text); @@ -2099,52 +2282,17 @@ const String String::fromUTF8 (const char* const buffer, int bufferSizeBytes) if (buffer == 0) return empty; - const int len = (int) (bufferSizeBytes >= 0 ? CharPointer_UTF8 (buffer).lengthUpTo (bufferSizeBytes) - : CharPointer_UTF8 (buffer).length()); - - String result (Preallocation (len + 1)); - CharPointerType (result.text).writeWithCharLimit (CharPointer_UTF8 (buffer), len + 1); - return result; -} - -//============================================================================== -const char* String::toCString() const -{ - #if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID - if (isEmpty()) - return reinterpret_cast (text.getAddress()); - - const int len = getNumBytesAsCString(); - char* const extraSpace = static_cast (createSpaceAtEndOfBuffer (len + 1)); - wcstombs (extraSpace, text, len); - extraSpace [len] = 0; - return extraSpace; - #else - return toUTF8(); - #endif -} + CharPointer_UTF8 b (buffer); -int String::getNumBytesAsCString() const throw() -{ - #if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID - return (int) wcstombs (0, text, 0); - #else - return getNumBytesAsUTF8(); - #endif -} + if (bufferSizeBytes < 0) + return String (b); -int String::copyToCString (char* destBuffer, const int maxBufferSizeBytes) const throw() -{ - #if JUCE_NATIVE_WCHAR_IS_UTF32 && ! JUCE_ANDROID - const int numBytes = (int) wcstombs (destBuffer, text, maxBufferSizeBytes); + const size_t numChars = b.lengthUpTo (CharPointer_UTF8 (buffer + bufferSizeBytes)); - if (destBuffer != 0 && numBytes >= 0) - destBuffer [numBytes] = 0; + if (numChars > 0) + return String (b, numChars); - return numBytes; - #else - return copyToUTF8 (destBuffer, maxBufferSizeBytes); - #endif + return String::empty; } #if JUCE_MSVC @@ -2168,7 +2316,8 @@ void String::Concatenator::append (const String& s) if (len > 0) { - result.preallocateStorage (nextIndex + len); + size_t extraBytes = s.getCharPointer().sizeInBytes(); + result.preallocateBytes (nextIndex + extraBytes); CharPointerType (result.text + nextIndex).writeAll (s.text); nextIndex += len; } @@ -2258,8 +2407,8 @@ public: expect (s1.startsWith ("ab") && s1.startsWith ("abcd") && ! s1.startsWith ("abcde")); expect (s1.startsWithIgnoreCase ("aB") && s1.endsWithIgnoreCase ("CD")); expect (s1.endsWith ("bcd") && ! s1.endsWith ("aabcd")); - expect (s1.indexOf (String::empty) == 0); - expect (s1.indexOfIgnoreCase (String::empty) == 0); + expectEquals (s1.indexOf (String::empty), 0); + expectEquals (s1.indexOfIgnoreCase (String::empty), 0); expect (s1.startsWith (String::empty) && s1.endsWith (String::empty) && s1.contains (String::empty)); expect (s1.contains ("cd") && s1.contains ("ab") && s1.contains ("abcd")); expect (s1.containsChar ('a')); diff --git a/src/text/juce_String.h b/src/text/juce_String.h index 05c31bc2f4..60ccdcd835 100644 --- a/src/text/juce_String.h +++ b/src/text/juce_String.h @@ -28,6 +28,10 @@ #include "juce_CharacterFunctions.h" +#ifndef JUCE_STRING_UTF_TYPE + #define JUCE_STRING_UTF_TYPE 8 +#endif + #if JUCE_MSVC #pragma warning (push) #pragma warning (disable: 4514 4996) @@ -45,7 +49,6 @@ class OutputStream; - //============================================================================== /** The JUCE String class! @@ -111,12 +114,35 @@ public: */ String (const juce_wchar* unicodeText, size_t maxChars); + #if ! JUCE_NATIVE_WCHAR_IS_UTF32 + /** Creates a string from a UTF-16 character string */ + String (const wchar_t* text); + + /** Creates a string from a UTF-16 character string */ + String (const wchar_t* text, size_t maxChars); + #endif + + //============================================================================== /** Creates a string from a UTF-8 character string */ String (const CharPointer_UTF8& text); + /** Creates a string from a UTF-8 character string */ + String (const CharPointer_UTF8& text, size_t maxChars); + + /** Creates a string from a UTF-8 character string */ + String (const CharPointer_UTF8& start, const CharPointer_UTF8& end); + + //============================================================================== /** Creates a string from a UTF-16 character string */ String (const CharPointer_UTF16& text); + /** Creates a string from a UTF-16 character string */ + String (const CharPointer_UTF16& text, size_t maxChars); + + /** Creates a string from a UTF-16 character string */ + String (const CharPointer_UTF16& start, const CharPointer_UTF16& end); + + //============================================================================== /** Creates a string from a UTF-32 character string */ String (const CharPointer_UTF32& text); @@ -126,17 +152,11 @@ public: /** Creates a string from a UTF-32 character string */ String (const CharPointer_UTF32& start, const CharPointer_UTF32& end); + //============================================================================== /** Creates a string from an ASCII character string */ String (const CharPointer_ASCII& text); - #if ! JUCE_NATIVE_WCHAR_IS_UTF32 - /** Creates a string from a UTF-16 character string */ - String (const wchar_t* text); - - /** Creates a string from a UTF-16 character string */ - String (const wchar_t* text, size_t maxChars); - #endif - + //============================================================================== /** Creates a string from a single character. */ static const String charToString (juce_wchar character); @@ -151,8 +171,27 @@ public: */ static const String empty; - /** This is the character encoding type used internally to store the string. */ + /** This is the character encoding type used internally to store the string. + + By setting the value of JUCE_STRING_UTF_TYPE to 8, 16, or 32, you can change the + internal storage format of the String class. UTF-8 uses the least space (if your strings + contain few extended characters), but call operator[] involves iterating the string to find + the required index. UTF-32 provides instant random access to its characters, but uses 4 bytes + per character to store them. UTF-16 uses more space than UTF-8 and is also slow to index, + but is the native wchar_t format used in Windows. + + It doesn't matter too much which format you pick, because the toUTF8(), toUTF16() and + toUTF32() methods let you access the string's content in any of the other formats. + */ + #if (JUCE_STRING_UTF_TYPE == 32) typedef CharPointer_UTF32 CharPointerType; + #elif (JUCE_STRING_UTF_TYPE == 16) + typedef CharPointer_UTF16 CharPointerType; + #elif (JUCE_STRING_UTF_TYPE == 8) + typedef CharPointer_UTF8 CharPointerType; + #else + #error "You must set the value of JUCE_STRING_UTF_TYPE to be either 8, 16, or 32!" + #endif //============================================================================== /** Generates a probably-unique 32-bit hashcode from this string. */ @@ -204,13 +243,21 @@ public: { if (textToAppend.getAddress() != 0) { - const size_t numExtraChars = textToAppend.lengthUpTo (maxCharsToTake); + size_t extraBytesNeeded = 0; + size_t numChars = 0; + + for (CharPointer t (textToAppend); numChars < maxCharsToTake && ! t.isEmpty();) + { + extraBytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); + ++numChars; + } - if (numExtraChars > 0) + if (numChars > 0) { - const int oldLen = length(); - preallocateStorage (oldLen + numExtraChars); - CharPointerType (text + oldLen).writeWithCharLimit (textToAppend, (int) (numExtraChars + 1)); + const size_t byteOffsetOfNull = getByteOffsetOfEnd(); + + preallocateBytes (byteOffsetOfNull + extraBytesNeeded); + CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)).writeWithCharLimit (textToAppend, (int) (numChars + 1)); } } } @@ -225,13 +272,17 @@ public: { if (textToAppend.getAddress() != 0) { - const size_t numExtraChars = textToAppend.length(); + size_t extraBytesNeeded = 0; + + for (CharPointer t (textToAppend); ! t.isEmpty();) + extraBytesNeeded += CharPointerType::getBytesRequiredFor (t.getAndAdvance()); - if (numExtraChars > 0) + if (extraBytesNeeded > 0) { - const int oldLen = length(); - preallocateStorage (oldLen + numExtraChars); - CharPointerType (text + oldLen).writeAll (textToAppend); + const size_t byteOffsetOfNull = getByteOffsetOfEnd(); + + preallocateBytes (byteOffsetOfNull + extraBytesNeeded); + CharPointerType (addBytesToPointer (text.getAddress(), (int) byteOffsetOfNull)).writeAll (textToAppend); } } } @@ -430,18 +481,14 @@ public: // Substring location methods.. /** Searches for a character inside this string. - Uses a case-sensitive comparison. - @returns the index of the first occurrence of the character in this string, or -1 if it's not found. */ int indexOfChar (juce_wchar characterToLookFor) const throw(); /** Searches for a character inside this string. - Uses a case-sensitive comparison. - @param startIndex the index from which the search should proceed @param characterToLookFor the character to look for @returns the index of the first occurrence of the character in this @@ -466,67 +513,54 @@ public: bool ignoreCase = false) const throw(); /** Searches for a substring within this string. - Uses a case-sensitive 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 indexOf (const String& text) const throw(); + int indexOf (const String& textToLookFor) const throw(); /** Searches for a substring within this string. - Uses a case-sensitive comparison. - @param startIndex the index from which the search should proceed @param textToLookFor the string to search for @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 throw(); + int indexOf (int startIndex, const String& textToLookFor) const throw(); /** 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 throw(); /** Searches for a substring within this string. - Uses a case-insensitive comparison. - @param startIndex the index from which the search should proceed @param textToLookFor the string to search for @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 throw(); + int indexOfIgnoreCase (int startIndex, const String& textToLookFor) const throw(); /** Searches for a character inside this string (working backwards from the end of the string). - Uses a case-sensitive comparison. - - @returns the index of the last occurrence of the character in this - string, or -1 if it's not found. + @returns the index of the last occurrence of the character in this string, or -1 if it's not found. */ int lastIndexOfChar (juce_wchar character) const throw(); /** Searches for a substring inside this string (working backwards from the end of the string). - Uses a case-sensitive comparison. - - @returns the index of the start of the last occurrence of the - substring within this string, or -1 if it's not found. + @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 throw(); /** 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. + @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 throw(); @@ -550,8 +584,15 @@ public: // Substring extraction and manipulation methods.. /** Returns the character at this index in the string. + In a release build, no checks are made to see if the index is within a valid range, so be + careful! In a debug build, the index is checked and an assertion fires if it's out-of-range. - No checks are made to see if the index is within a valid range, so be careful! + Also beware that depending on the encoding format that the string is using internally, this + method may execute in either O(1) or O(n) time, so be careful when using it in your algorithms. + If you're scanning through a string to inspect its characters, you should never use this operator + for random access, it's far more efficient to call getCharPointer() to return a pointer, and + then to use that to iterate the string. + @see getCharPointer */ const juce_wchar operator[] (int index) const throw(); @@ -999,15 +1040,6 @@ public: int size, int groupSize = 1); - //============================================================================== - /** Returns a unicode version of this string. - - Because it returns a reference to the string's internal data, the pointer - that is returned must not be stored anywhere, as it can become invalid whenever - any string methods (even some const ones!) are called. - */ - inline operator const juce_wchar*() const throw() { return toUTF32().getAddress(); } - //============================================================================== /** Returns the character pointer currently being used to store this string. @@ -1051,7 +1083,7 @@ public: @see getCharPointer, toUTF8, toUTF16 */ - inline CharPointer_UTF32 toUTF32() const throw() { return text; } + CharPointer_UTF32 toUTF32() const; //============================================================================== /** Creates a String from a UTF-8 encoded buffer. @@ -1100,35 +1132,22 @@ public: */ int copyToUTF16 (CharPointer_UTF16::CharType* destBuffer, int maxBufferSizeBytes) const throw(); - //============================================================================== - /** Returns a version of this string using the default 8-bit multi-byte system encoding. - - Because it returns a reference to the string's internal data, the pointer - that is returned must not be stored anywhere, as it can be deleted whenever the - string changes. - - @see getNumBytesAsCString, copyToCString, toUTF8 - */ - const char* toCString() const; + /** Copies the string to a buffer as UTF-16 characters. - /** Returns the number of bytes required to represent this string as C-string. - The number returned does NOT include the trailing zero. - Note that you can also get this value by using CharPointer_UTF8::getBytesRequiredFor (myString.getCharPointer()) - */ - int getNumBytesAsCString() const throw(); + Returns the number of bytes copied to the buffer, including the terminating null + character. - /** Copies the string to a buffer. + To find out how many bytes you need to store this string as UTF-32, you can call + CharPointer_UTF32::getBytesRequiredFor (myString.getCharPointer()) - @param destBuffer the place to copy it to; if this is a null pointer, - the method just returns the number of bytes required - (including the terminating null character). - @param maxBufferSizeBytes the size of the destination buffer, in bytes. If the - string won't fit, it'll put in as many as it can while - still allowing for a terminating null char at the end, and - will return the number of bytes that were actually used. + @param destBuffer the place to copy it to; if this is a null pointer, the method just + returns the number of bytes required (including the terminating null character). + @param maxBufferSizeBytes the size of the destination buffer, in bytes. If the string won't fit, it'll + put in as many as it can while still allowing for a terminating null char at the + end, and will return the number of bytes that were actually used. + @see CharPointer_UTF32::writeWithDestByteLimit */ - int copyToCString (char* destBuffer, int maxBufferSizeBytes) const throw(); - + int copyToUTF32 (CharPointer_UTF32::CharType* destBuffer, int maxBufferSizeBytes) const throw(); //============================================================================== /** Increases the string's internally allocated storage. @@ -1141,11 +1160,11 @@ public: beforehand, so that these methods won't have to keep resizing the string to append the extra characters. - @param numCharsNeeded the number of characters to allocate storage for. If this + @param numBytesNeeded the number of bytes to allocate storage for. If this value is less than the currently allocated size, it will have no effect. */ - void preallocateStorage (size_t numCharsNeeded); + void preallocateBytes (size_t numBytesNeeded); /** Swaps the contents of this string with another one. This is a very fast operation, as no allocation or copying needs to be done. @@ -1182,20 +1201,18 @@ private: CharPointerType text; //============================================================================== - struct Preallocation + struct PreallocationBytes { - explicit Preallocation (size_t); - size_t numChars; + explicit PreallocationBytes (size_t); + size_t numBytes; }; // This constructor preallocates a certain amount of memory - explicit String (const Preallocation&); - String (const String& stringToCopy, size_t charsToAllocate); - - void appendFixedLength (const juce_wchar* text, int numExtraChars); + explicit String (const PreallocationBytes&); + JUCE_DEPRECATED (String (const String& stringToCopy, size_t charsToAllocate)); - void enlarge (size_t newTotalNumChars); - void* createSpaceAtEndOfBuffer (size_t numExtraBytes) const; + void appendFixedLength (const char* text, int numExtraChars); + size_t getByteOffsetOfEnd() const throw(); // This private cast operator should prevent strings being accidentally cast // to bools (this is possible because the compiler can add an implicit cast diff --git a/src/text/juce_StringArray.cpp b/src/text/juce_StringArray.cpp index 21db37e019..fda78b01cc 100644 --- a/src/text/juce_StringArray.cpp +++ b/src/text/juce_StringArray.cpp @@ -328,14 +328,14 @@ const String StringArray::joinIntoString (const String& separator, int start, in if (start == last - 1) return strings.getReference (start); - const int separatorLen = separator.length(); - int charsNeeded = separatorLen * (last - start - 1); + const size_t separatorBytes = separator.getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); + size_t bytesNeeded = separatorBytes * (last - start - 1); for (int i = start; i < last; ++i) - charsNeeded += strings.getReference(i).length(); + bytesNeeded += strings.getReference(i).getCharPointer().sizeInBytes() - sizeof (String::CharPointerType::CharType); String result; - result.preallocateStorage (charsNeeded); + result.preallocateBytes (bytesNeeded); String::CharPointerType dest (result.getCharPointer()); @@ -346,7 +346,7 @@ const String StringArray::joinIntoString (const String& separator, int start, in if (! s.isEmpty()) dest.writeAll (s.getCharPointer()); - if (++start < last && separatorLen > 0) + if (++start < last && separatorBytes > 0) dest.writeAll (separator.getCharPointer()); } diff --git a/src/text/juce_StringPool.cpp b/src/text/juce_StringPool.cpp index 9b3871e1f3..5c21f78e74 100644 --- a/src/text/juce_StringPool.cpp +++ b/src/text/juce_StringPool.cpp @@ -110,7 +110,7 @@ int StringPool::size() const throw() return strings.size(); } -const juce_wchar* StringPool::operator[] (const int index) const throw() +const String::CharPointerType StringPool::operator[] (const int index) const throw() { return strings [index].getCharPointer(); } diff --git a/src/text/juce_StringPool.h b/src/text/juce_StringPool.h index c1c275d33c..e3631477e4 100644 --- a/src/text/juce_StringPool.h +++ b/src/text/juce_StringPool.h @@ -81,7 +81,7 @@ public: int size() const throw(); /** Returns one of the strings in the pool, by index. */ - const juce_wchar* operator[] (int index) const throw(); + const String::CharPointerType operator[] (int index) const throw(); private: Array strings; diff --git a/src/text/juce_XmlDocument.cpp b/src/text/juce_XmlDocument.cpp index 2ef1498bfe..0cc216bb20 100644 --- a/src/text/juce_XmlDocument.cpp +++ b/src/text/juce_XmlDocument.cpp @@ -209,7 +209,7 @@ void XmlDocument::skipHeader() return; #if JUCE_DEBUG - const String header ((input + headerStart).getAddress(), headerEnd - headerStart); + const String header (input + headerStart, headerEnd - headerStart); const String encoding (header.fromFirstOccurrenceOf ("encoding", false, true) .fromFirstOccurrenceOf ("=", false, false) .fromFirstOccurrenceOf ("\"", false, false) @@ -252,7 +252,7 @@ void XmlDocument::skipHeader() --n; } - dtdText = String (docType.getAddress(), (int) (input.getAddress() - (docType.getAddress() + 1))).trim(); + dtdText = String (docType, (int) (input.getAddress() - (docType.getAddress() + 1))).trim(); } void XmlDocument::skipNextWhiteSpace() @@ -383,7 +383,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) } } - node = new XmlElement (String (input.getAddress(), tagLen)); + node = new XmlElement (String (input, tagLen)); input += tagLen; LinkedListPointer::Appender attributeAppender (node->attributes); @@ -433,7 +433,7 @@ XmlElement* XmlDocument::readNextElement (const bool alsoParseSubElements) if (nextChar == '"' || nextChar == '\'') { XmlElement::XmlAttributeNode* const newAtt - = new XmlElement::XmlAttributeNode (String (attNameStart.getAddress(), attNameLen), + = new XmlElement::XmlAttributeNode (String (attNameStart, attNameLen), String::empty); readQuotedString (newAtt->value); @@ -517,7 +517,7 @@ void XmlDocument::readChildElements (XmlElement* parent) ++len; } - childAppender.append (XmlElement::createTextElement (String (inputStart.getAddress(), len))); + childAppender.append (XmlElement::createTextElement (String (inputStart, len))); } else { @@ -713,7 +713,7 @@ void XmlDocument::readEntity (String& result) { input += closingSemiColon + 1; - result += expandExternalEntity (String (entityNameStart.getAddress(), closingSemiColon)); + result += expandExternalEntity (String (entityNameStart, closingSemiColon)); } } } @@ -728,10 +728,12 @@ const String XmlDocument::expandEntity (const String& ent) if (ent[0] == '#') { - if (ent[1] == 'x' || ent[1] == 'X') + const juce_wchar char1 = ent[1]; + + if (char1 == 'x' || char1 == 'X') return String::charToString (static_cast (ent.substring (2).getHexValue32())); - if (ent[1] >= '0' && ent[1] <= '9') + if (char1 >= '0' && char1 <= '9') return String::charToString (static_cast (ent.substring (1).getIntValue())); setLastError ("illegal escape sequence", false);