Browse Source

Moved out split from the string class and made an in-place version.

David Piuva 5 years ago
parent
commit
ab070e35d1

+ 1 - 1
Source/DFPSR/api/configAPI.cpp

@@ -26,7 +26,7 @@
 using namespace dsr;
 using namespace dsr;
 
 
 void dsr::config_parse_ini(const ReadableString& content, ConfigIniCallback receiverLambda) {
 void dsr::config_parse_ini(const ReadableString& content, ConfigIniCallback receiverLambda) {
-	List<ReadableString> lines = content.split(U'\n');
+	List<ReadableString> lines = string_split(content, U'\n');
 	String block = U"";
 	String block = U"";
 	for (int l = 0; l < lines.length(); l++) {
 	for (int l = 0; l < lines.length(); l++) {
 		// Get the current line
 		// Get the current line

+ 136 - 72
Source/DFPSR/base/text.cpp

@@ -91,76 +91,6 @@ ReadableString ReadableString::after(int exclusiveStart) const {
 	return this->from(exclusiveStart + 1);
 	return this->from(exclusiveStart + 1);
 }
 }
 
 
-List<ReadableString> ReadableString::split(DsrChar separator) const {
-	List<ReadableString> result;
-	int lineStart = 0;
-	for (int i = 0; i < this->length(); i++) {
-		DsrChar c = this->readSection[i];
-		if (c == separator) {
-			result.push(this->exclusiveRange(lineStart, i));
-			lineStart = i + 1;
-		}
-	}
-	if (this->length() > lineStart) {
-		result.push(this->exclusiveRange(lineStart, this->length()));;
-	}
-	return result;
-}
-
-int64_t ReadableString::toInteger() const {
-	int64_t result;
-	bool negated;
-	result = 0;
-	negated = false;
-	for (int i = 0; i < this->length(); i++) {
-		DsrChar c = this->readSection[i];
-		if (c == '-' || c == '~') {
-			negated = !negated;
-		} else if (c >= '0' && c <= '9') {
-			result = (result * 10) + (int)(c - '0');
-		} else if (c == ',' || c == '.') {
-			// Truncate any decimals by ignoring them
-			break;
-		}
-	}
-	if (negated) {
-		return -result;
-	} else {
-		return result;
-	}
-}
-
-double ReadableString::toDouble() const {
-	double result;
-	bool negated;
-	bool reachedDecimal;
-	int digitDivider;
-	result = 0.0;
-	negated = false;
-	reachedDecimal = false;
-	digitDivider = 1;
-	for (int i = 0; i < this->length(); i++) {
-		DsrChar c = this->readSection[i];
-		if (c == '-' || c == '~') {
-			negated = !negated;
-		} else if (c >= '0' && c <= '9') {
-			if (reachedDecimal) {
-				digitDivider = digitDivider * 10;
-				result = result + ((double)(c - '0') / (double)digitDivider);
-			} else {
-				result = (result * 10) + (double)(c - '0');
-			}
-		} else if (c == ',' || c == '.') {
-			reachedDecimal = true;
-		}
-	}
-	if (negated) {
-		return -result;
-	} else {
-		return result;
-	}
-}
-
 String& Printable::toStream(String& target) const {
 String& Printable::toStream(String& target) const {
 	return this->toStreamIndented(target, U"");
 	return this->toStreamIndented(target, U"");
 }
 }
@@ -194,6 +124,8 @@ std::string Printable::toStdString() const {
 	return result.str();
 	return result.str();
 }
 }
 
 
+Printable::~Printable() {}
+
 bool dsr::string_match(const ReadableString& a, const ReadableString& b) {
 bool dsr::string_match(const ReadableString& a, const ReadableString& b) {
 	if (a.length() != b.length()) {
 	if (a.length() != b.length()) {
 		return false;
 		return false;
@@ -534,8 +466,6 @@ DsrChar ReadableString::read(int index) const {
 
 
 DsrChar ReadableString::operator[] (int index) const { return this->read(index); }
 DsrChar ReadableString::operator[] (int index) const { return this->read(index); }
 
 
-Printable::~Printable() {}
-
 ReadableString::ReadableString() {}
 ReadableString::ReadableString() {}
 ReadableString::~ReadableString() {}
 ReadableString::~ReadableString() {}
 
 
@@ -769,3 +699,137 @@ String& dsr::string_toStreamIndented(String& target, const uint8_t& value, const
 void dsr::throwErrorMessage(const String& message) {
 void dsr::throwErrorMessage(const String& message) {
 	throw std::runtime_error(message.toStdString());
 	throw std::runtime_error(message.toStdString());
 }
 }
+
+void dsr::string_split_inPlace(List<ReadableString> &target, const ReadableString& source, DsrChar separator, bool appendResult) {
+	if (!appendResult) {
+		target.clear();
+	}
+	int sectionStart = 0;
+	for (int i = 0; i < source.length(); i++) {
+		DsrChar c = source[i];
+		if (c == separator) {
+			target.push(source.exclusiveRange(sectionStart, i));
+			sectionStart = i + 1;
+		}
+	}
+	if (source.length() > sectionStart) {
+		target.push(source.exclusiveRange(sectionStart, source.length()));;
+	}
+}
+
+List<ReadableString> dsr::string_split(const ReadableString& source, DsrChar separator) {
+	List<ReadableString> result;
+	string_split_inPlace(result, source, separator);
+	return result;
+}
+
+// TODO: Delete
+int64_t ReadableString::toInteger() const {
+	int64_t result;
+	bool negated;
+	result = 0;
+	negated = false;
+	for (int i = 0; i < this->length(); i++) {
+		DsrChar c = this->readSection[i];
+		if (c == '-' || c == '~') {
+			negated = !negated;
+		} else if (c >= '0' && c <= '9') {
+			result = (result * 10) + (int)(c - '0');
+		} else if (c == ',' || c == '.') {
+			// Truncate any decimals by ignoring them
+			break;
+		}
+	}
+	if (negated) {
+		return -result;
+	} else {
+		return result;
+	}
+}
+
+// TODO: Delete
+double ReadableString::toDouble() const {
+	double result;
+	bool negated;
+	bool reachedDecimal;
+	int digitDivider;
+	result = 0.0;
+	negated = false;
+	reachedDecimal = false;
+	digitDivider = 1;
+	for (int i = 0; i < this->length(); i++) {
+		DsrChar c = this->readSection[i];
+		if (c == '-' || c == '~') {
+			negated = !negated;
+		} else if (c >= '0' && c <= '9') {
+			if (reachedDecimal) {
+				digitDivider = digitDivider * 10;
+				result = result + ((double)(c - '0') / (double)digitDivider);
+			} else {
+				result = (result * 10) + (double)(c - '0');
+			}
+		} else if (c == ',' || c == '.') {
+			reachedDecimal = true;
+		}
+	}
+	if (negated) {
+		return -result;
+	} else {
+		return result;
+	}
+}
+
+int64_t dsr::string_toInteger(const ReadableString& source) {
+	int64_t result;
+	bool negated;
+	result = 0;
+	negated = false;
+	for (int i = 0; i < source.length(); i++) {
+		DsrChar c = source[i];
+		if (c == '-' || c == '~') {
+			negated = !negated;
+		} else if (c >= '0' && c <= '9') {
+			result = (result * 10) + (int)(c - '0');
+		} else if (c == ',' || c == '.') {
+			// Truncate any decimals by ignoring them
+			break;
+		}
+	}
+	if (negated) {
+		return -result;
+	} else {
+		return result;
+	}
+}
+
+double dsr::string_toDouble(const ReadableString& source) {
+	double result;
+	bool negated;
+	bool reachedDecimal;
+	int digitDivider;
+	result = 0.0;
+	negated = false;
+	reachedDecimal = false;
+	digitDivider = 1;
+	for (int i = 0; i < source.length(); i++) {
+		DsrChar c = source[i];
+		if (c == '-' || c == '~') {
+			negated = !negated;
+		} else if (c >= '0' && c <= '9') {
+			if (reachedDecimal) {
+				digitDivider = digitDivider * 10;
+				result = result + ((double)(c - '0') / (double)digitDivider);
+			} else {
+				result = (result * 10) + (double)(c - '0');
+			}
+		} else if (c == ',' || c == '.') {
+			reachedDecimal = true;
+		}
+	}
+	if (negated) {
+		return -result;
+	} else {
+		return result;
+	}
+}
+

+ 72 - 42
Source/DFPSR/base/text.h

@@ -89,9 +89,6 @@ public:
 	ReadableString until(int inclusiveEnd) const;
 	ReadableString until(int inclusiveEnd) const;
 	ReadableString from(int inclusiveStart) const;
 	ReadableString from(int inclusiveStart) const;
 	ReadableString after(int exclusiveStart) const;
 	ReadableString after(int exclusiveStart) const;
-	// Split into a list of strings without allocating any new text buffers
-	//   The result can be kept after the original string has been freed, because the buffer is reference counted
-	List<ReadableString> split(DsrChar separator) const;
 	// Value conversion
 	// Value conversion
 	int64_t toInteger() const;
 	int64_t toInteger() const;
 	double toDouble() const;
 	double toDouble() const;
@@ -189,7 +186,64 @@ String& string_toStreamIndented(String& target, const uint16_t& value, const Rea
 String& string_toStreamIndented(String& target, const int8_t& value, const ReadableString& indentation);
 String& string_toStreamIndented(String& target, const int8_t& value, const ReadableString& indentation);
 String& string_toStreamIndented(String& target, const uint8_t& value, const ReadableString& indentation);
 String& string_toStreamIndented(String& target, const uint8_t& value, const ReadableString& indentation);
 
 
-// Procedural API
+// Templates reused for all types
+// The source must inherit from Printable or have its own string_feedIndented overload
+template<typename T>
+String& string_toStream(String& target, const T& source) {
+	return string_toStreamIndented(target, source, U"");
+}
+template<typename T>
+String string_toStringIndented(const T& source, const ReadableString& indentation) {
+	String result;
+	string_toStreamIndented(result, source, indentation);
+	return result;
+}
+template<typename T>
+String string_toString(const T& source) {
+	return string_toStringIndented(source, U"");
+}
+template<typename T>
+std::ostream& string_toStreamIndented(std::ostream& target, const T& source, const ReadableString& indentation) {
+	return target << string_toStringIndented(source, indentation);
+}
+template<typename T>
+std::ostream& string_toStream(std::ostream& target, const T& source) {
+	return target << string_toString(source);
+}
+
+
+// ---------------- Procedural API ----------------
+
+
+// Post-condition:
+//   Returns a list of strings from source by splitting along separator.
+// The separating characters are excluded from the resulting strings.
+// The number of strings returned in the list will equal the number of separating characters plus one, so the result may contain empty strings.
+// Each string in the list reuses memory from the input string using reference counting, but the list itself will be allocated.
+List<ReadableString> string_split(const ReadableString& source, DsrChar separator);
+// Use string_split_inPlace instead of string_split if you want to reuse the memory of an existing list.
+//   It will then only allocate when running out of buffer space.
+// Side-effects:
+//   Fills the target list with strings from source by splitting along separator.
+//   If appendResult is false (default), any pre-existing elements in the target list will be cleared before writing the result.
+//   If appendResult is true, the result is appended to the existing target list.
+void string_split_inPlace(List<ReadableString> &target, const ReadableString& source, DsrChar separator, bool appendResult = false);
+
+// Post-condition: Returns the integer representation of source.
+// The result is signed, because the input might unexpectedly have a negation sign.
+// The result is large, so that one can easily check the range before assigning to a smaller integer type.
+int64_t string_toInteger(const ReadableString& source);
+// Post-condition: Returns the double precision floating-point representation of source.
+double string_toDouble(const ReadableString& source);
+// Pre-condition: Content must contain an integer, or unexpected things may happen.
+// Post-condition: Returns the numerical integer value of content while ignoring any forbidden characters.
+// Examples:
+//   string_parseInteger(U"-25") == -25 // Good case
+//   string_parseInteger(U" -25 ") == -25 // Still works
+//   string_parseInteger(U" 10x10 ") == 1010 // Any digits are simply added in order while ignoring the rest
+int64_t string_parseInteger(const ReadableString& content);
+// Post-condition: Returns the double-precision floating-point approximation of content's numerical value
+double string_parseDouble(const ReadableString& content);
 
 
 // Post-condition:
 // Post-condition:
 //   Returns the content of the file referred to be filename.
 //   Returns the content of the file referred to be filename.
@@ -198,28 +252,23 @@ String& string_toStreamIndented(String& target, const uint8_t& value, const Read
 String string_load(const ReadableString& filename, bool mustExist = true);
 String string_load(const ReadableString& filename, bool mustExist = true);
 // Side-effect: Saves content to filename.
 // Side-effect: Saves content to filename.
 void string_save(const ReadableString& filename, const ReadableString& content);
 void string_save(const ReadableString& filename, const ReadableString& content);
+
 // Post-condition: Returns true iff strings a and b are exactly equal.
 // Post-condition: Returns true iff strings a and b are exactly equal.
 bool string_match(const ReadableString& a, const ReadableString& b);
 bool string_match(const ReadableString& a, const ReadableString& b);
 // Post-condition: Returns true iff strings a and b are roughly equal using a case insensitive match.
 // Post-condition: Returns true iff strings a and b are roughly equal using a case insensitive match.
 bool string_caseInsensitiveMatch(const ReadableString& a, const ReadableString& b);
 bool string_caseInsensitiveMatch(const ReadableString& a, const ReadableString& b);
+
 // Post-condition: Returns text converted to upper case.
 // Post-condition: Returns text converted to upper case.
 String string_upperCase(const ReadableString &text);
 String string_upperCase(const ReadableString &text);
 // Post-condition: Returns text converted to lower case.
 // Post-condition: Returns text converted to lower case.
 String string_lowerCase(const ReadableString &text);
 String string_lowerCase(const ReadableString &text);
+
 // Post-condition: Returns a clone of text without any white-space (space, tab and carriage-return).
 // Post-condition: Returns a clone of text without any white-space (space, tab and carriage-return).
 String string_removeAllWhiteSpace(const ReadableString &text);
 String string_removeAllWhiteSpace(const ReadableString &text);
 // Post-condition: Returns a sub-set of text without surrounding white-space (space, tab and carriage-return).
 // Post-condition: Returns a sub-set of text without surrounding white-space (space, tab and carriage-return).
 // Unlike string_removeAllWhiteSpace, string_removeOuterWhiteSpace does not require allocating a new buffer.
 // Unlike string_removeAllWhiteSpace, string_removeOuterWhiteSpace does not require allocating a new buffer.
 ReadableString string_removeOuterWhiteSpace(const ReadableString &text);
 ReadableString string_removeOuterWhiteSpace(const ReadableString &text);
-// Pre-condition: Content must contain an integer, or unexpected things may happen.
-// Post-condition: Returns the numerical integer value of content while ignoring any forbidden characters.
-// Examples:
-//   string_parseInteger(U"-25") == -25 // Good case
-//   string_parseInteger(U" -25 ") == -25 // Still works
-//   string_parseInteger(U" 10x10 ") == 1010 // Any digits are simply added in order while ignoring the rest
-int64_t string_parseInteger(const ReadableString& content);
-// Post-condition: Returns the double-precision floating-point approximation of content's numerical value
-double string_parseDouble(const ReadableString& content);
+
 // Post-condition: Returns rawText wrapped in a quote.
 // Post-condition: Returns rawText wrapped in a quote.
 // Special characters are included using escape characters, so that one can quote multiple lines but store it easily.
 // Special characters are included using escape characters, so that one can quote multiple lines but store it easily.
 String string_mangleQuote(const ReadableString &rawText);
 String string_mangleQuote(const ReadableString &rawText);
@@ -247,6 +296,10 @@ inline String string_combine(ARGS... args) {
 	return result;
 	return result;
 }
 }
 
 
+
+// ---------------- Infix syntax ----------------
+
+
 // Operations
 // Operations
 inline String operator+ (const ReadableString& a, const ReadableString& b) { return string_combine(a, b); }
 inline String operator+ (const ReadableString& a, const ReadableString& b) { return string_combine(a, b); }
 inline String operator+ (const char32_t* a, const ReadableString& b) { return string_combine(a, b); }
 inline String operator+ (const char32_t* a, const ReadableString& b) { return string_combine(a, b); }
@@ -257,6 +310,10 @@ inline String operator+ (const String& a, const char32_t* b) { return string_com
 inline String operator+ (const String& a, const ReadableString& b) { return string_combine(a, b); }
 inline String operator+ (const String& a, const ReadableString& b) { return string_combine(a, b); }
 inline String operator+ (const ReadableString& a, const String& b) { return string_combine(a, b); }
 inline String operator+ (const ReadableString& a, const String& b) { return string_combine(a, b); }
 
 
+
+// Methods used so often that they don't need to use the string_ prefix
+
+
 // Print information
 // Print information
 template<typename... ARGS>
 template<typename... ARGS>
 void printText(ARGS... args) {
 void printText(ARGS... args) {
@@ -286,35 +343,8 @@ void throwError(ARGS... args) {
 }
 }
 
 
 
 
-// ---------------- Overloaded serialization ----------------
-
-
-// Templates reused for all types
-// The source must inherit from Printable or have its own string_feedIndented overload
-template<typename T>
-String& string_toStream(String& target, const T& source) {
-	return string_toStreamIndented(target, source, U"");
-}
-template<typename T>
-String string_toStringIndented(const T& source, const ReadableString& indentation) {
-	String result;
-	string_toStreamIndented(result, source, indentation);
-	return result;
-}
-template<typename T>
-String string_toString(const T& source) {
-	return string_toStringIndented(source, U"");
-}
-template<typename T>
-std::ostream& string_toStreamIndented(std::ostream& target, const T& source, const ReadableString& indentation) {
-	return target << string_toStringIndented(source, indentation);
-}
-template<typename T>
-std::ostream& string_toStream(std::ostream& target, const T& source) {
-	return target << string_toString(source);
-}
-
-// ---------------- Below uses hard-coded portability for specific operating systems ----------------
+// ---------------- Hard-coded portability for specific operating systems ----------------
+// TODO: Try to find a place for this outside of the library, similar to how window managers were implemented
 
 
 
 
 // Get a path separator for the target operating system.
 // Get a path separator for the target operating system.

+ 2 - 2
Source/DFPSR/image/Color.cpp

@@ -42,7 +42,7 @@ ColorRgbI32 ColorRgbI32::mix(const ColorRgbI32& colorA, const ColorRgbI32& color
 	return (colorA * invWeight) + (colorB * weight);
 	return (colorA * invWeight) + (colorB * weight);
 }
 }
 ColorRgbI32::ColorRgbI32(const ReadableString &content) : red(0), green(0), blue(0) {
 ColorRgbI32::ColorRgbI32(const ReadableString &content) : red(0), green(0), blue(0) {
-	List<ReadableString> elements = content.split(U',');
+	List<ReadableString> elements = string_split(content, U',');
 	int givenChannels = elements.length();
 	int givenChannels = elements.length();
 	if (givenChannels >= 1) {
 	if (givenChannels >= 1) {
 		this-> red = string_parseInteger(elements[0]);
 		this-> red = string_parseInteger(elements[0]);
@@ -74,7 +74,7 @@ ColorRgbaI32 ColorRgbaI32::mix(const ColorRgbaI32& colorA, const ColorRgbaI32& c
 	return (colorA * invWeight) + (colorB * weight);
 	return (colorA * invWeight) + (colorB * weight);
 }
 }
 ColorRgbaI32::ColorRgbaI32(const ReadableString &content) : red(0), green(0), blue(0), alpha(255) {
 ColorRgbaI32::ColorRgbaI32(const ReadableString &content) : red(0), green(0), blue(0), alpha(255) {
-	List<ReadableString> elements = content.split(U',');
+	List<ReadableString> elements = string_split(content, U',');
 	int givenChannels = elements.length();
 	int givenChannels = elements.length();
 	if (givenChannels >= 1) {
 	if (givenChannels >= 1) {
 		this-> red = string_parseInteger(elements[0]);
 		this-> red = string_parseInteger(elements[0]);

+ 3 - 2
Source/DFPSR/machine/VirtualMachine.cpp

@@ -35,10 +35,11 @@ VirtualMachine::VirtualMachine(const ReadableString& code, const std::shared_ptr
 		printText("Starting media machine.\n");
 		printText("Starting media machine.\n");
 	#endif
 	#endif
 	this->methods.pushConstruct(U"<init>", 0, this->machineTypeCount);
 	this->methods.pushConstruct(U"<init>", 0, this->machineTypeCount);
-	List<ReadableString> lines = code.split(U'\n');
+	List<ReadableString> lines = string_split(code, U'\n');
 	#ifdef VIRTUAL_MACHINE_DEBUG_PRINT
 	#ifdef VIRTUAL_MACHINE_DEBUG_PRINT
 		printText("Reading assembly.\n");
 		printText("Reading assembly.\n");
 	#endif
 	#endif
+	List<ReadableString> arguments; // Re-using the buffer for in-place splitting
 	for (int l = 0; l < lines.length(); l++) {
 	for (int l = 0; l < lines.length(); l++) {
 		ReadableString currentLine = lines[l];
 		ReadableString currentLine = lines[l];
 		// If the line has a comment, then skip everything from #
 		// If the line has a comment, then skip everything from #
@@ -51,7 +52,7 @@ VirtualMachine::VirtualMachine(const ReadableString& code, const std::shared_ptr
 		if (colonIndex > -1) {
 		if (colonIndex > -1) {
 			ReadableString command = string_removeOuterWhiteSpace(currentLine.before(colonIndex));
 			ReadableString command = string_removeOuterWhiteSpace(currentLine.before(colonIndex));
 			ReadableString argumentLine = currentLine.after(colonIndex);
 			ReadableString argumentLine = currentLine.after(colonIndex);
-			List<ReadableString> arguments = argumentLine.split(U',');
+			string_split_inPlace(arguments, argumentLine, U',');
 			this->interpretMachineWord(command, arguments);
 			this->interpretMachineWord(command, arguments);
 		} else if (currentLine.length() > 0) {
 		} else if (currentLine.length() > 0) {
 			throwError("Unexpected line \"", currentLine, "\".\n");
 			throwError("Unexpected line \"", currentLine, "\".\n");

+ 1 - 1
Source/DFPSR/persistent/ClassFactory.cpp

@@ -138,7 +138,7 @@ std::shared_ptr<Persistent> dsr::createPersistentClass(const String &type, bool
 std::shared_ptr<Persistent> dsr::createPersistentClassFromText(const ReadableString &text) {
 std::shared_ptr<Persistent> dsr::createPersistentClassFromText(const ReadableString &text) {
 	std::shared_ptr<Persistent> rootObject, newObject;
 	std::shared_ptr<Persistent> rootObject, newObject;
 	List<std::shared_ptr<Persistent>> stack;
 	List<std::shared_ptr<Persistent>> stack;
-	List<ReadableString> lines = text.split(U'\n');
+	List<ReadableString> lines = string_split(text, U'\n');
 	for (int l = 0; l < lines.length(); l++) {
 	for (int l = 0; l < lines.length(); l++) {
 		ReadableString line = lines[l];
 		ReadableString line = lines[l];
 		int equalityIndex = line.findFirst('=');
 		int equalityIndex = line.findFirst('=');

+ 2 - 2
Source/SDK/sandbox/sprite/spriteAPI.cpp

@@ -34,7 +34,7 @@ struct SpriteConfig {
 				} else if (string_caseInsensitiveMatch(key, U"MaxBound")) {
 				} else if (string_caseInsensitiveMatch(key, U"MaxBound")) {
 					this->maxBound = parseFVector3D(value);
 					this->maxBound = parseFVector3D(value);
 				} else if (string_caseInsensitiveMatch(key, U"Points")) {
 				} else if (string_caseInsensitiveMatch(key, U"Points")) {
-					List<ReadableString> values = value.split(U',');
+					List<ReadableString> values = string_split(value, U',');
 					if (values.length() % 3 != 0) {
 					if (values.length() % 3 != 0) {
 						throwError("Points contained ", values.length(), " values, which is not evenly divisible by three!");
 						throwError("Points contained ", values.length(), " values, which is not evenly divisible by three!");
 					} else {
 					} else {
@@ -45,7 +45,7 @@ struct SpriteConfig {
 						}
 						}
 					}
 					}
 				} else if (string_caseInsensitiveMatch(key, U"TriangleIndices")) {
 				} else if (string_caseInsensitiveMatch(key, U"TriangleIndices")) {
-					List<ReadableString> values = value.split(U',');
+					List<ReadableString> values = string_split(value, U',');
 					if (values.length() % 3 != 0) {
 					if (values.length() % 3 != 0) {
 						throwError("TriangleIndices contained ", values.length(), " values, which is not evenly divisible by three!");
 						throwError("TriangleIndices contained ", values.length(), " values, which is not evenly divisible by three!");
 					} else {
 					} else {

+ 1 - 1
Source/SDK/sandbox/sprite/spriteAPI.h

@@ -12,7 +12,7 @@ namespace dsr {
 
 
 // TODO: Make into a constructor for each vector type
 // TODO: Make into a constructor for each vector type
 inline FVector3D parseFVector3D(const ReadableString& content) {
 inline FVector3D parseFVector3D(const ReadableString& content) {
-	List<ReadableString> args = content.split(U',');
+	List<ReadableString> args = string_split(content, U',');
 	if (args.length() != 3) {
 	if (args.length() != 3) {
 		printText("Expected a vector of three decimal values.\n");
 		printText("Expected a vector of three decimal values.\n");
 		return FVector3D();
 		return FVector3D();

+ 16 - 18
Source/SDK/sandbox/tool.cpp

@@ -387,7 +387,7 @@ static void loadPlyModel(ParserState& state, const ReadableString& content, bool
 	int startPointIndex = model_getNumberOfPoints(targetModel);
 	int startPointIndex = model_getNumberOfPoints(targetModel);
 	int targetPart = shadow ? 0 : state.part;
 	int targetPart = shadow ? 0 : state.part;
 	// Split lines
 	// Split lines
-	List<ReadableString> lines = content.split(U'\n');
+	List<ReadableString> lines = string_split(content, U'\n');
 	List<PlyElement> elements;
 	List<PlyElement> elements;
 	bool readingContent = false; // True after passing end_header
 	bool readingContent = false; // True after passing end_header
 	int elementIndex = -1; // current member of elements
 	int elementIndex = -1; // current member of elements
@@ -408,7 +408,8 @@ static void loadPlyModel(ParserState& state, const ReadableString& content, bool
 	for (int l = 0; l < lines.length(); l++) {
 	for (int l = 0; l < lines.length(); l++) {
 		// Tokenize the current line
 		// Tokenize the current line
 		ReadableString currentLine = string_removeOuterWhiteSpace(lines[l]);
 		ReadableString currentLine = string_removeOuterWhiteSpace(lines[l]);
-		List<ReadableString> tokens = currentLine.split(U' ');
+		List<ReadableString> tokens;
+		string_split_inPlace(tokens, currentLine, U' ');
 		if (tokens.length() > 0 && !string_caseInsensitiveMatch(tokens[0], U"COMMENT")) {
 		if (tokens.length() > 0 && !string_caseInsensitiveMatch(tokens[0], U"COMMENT")) {
 			if (readingContent) {
 			if (readingContent) {
 				// Parse geometry
 				// Parse geometry
@@ -721,22 +722,9 @@ static void parse_shape(ParserState& state, List<ReadableString>& args, bool sha
 	}
 	}
 }
 }
 
 
-static void parse_command(ParserState& state, const ReadableString& command, const ReadableString& argContent) {
-	List<ReadableString> args = argContent.split(U',');
-	for (int a = 0; a < args.length(); a++) {
-		args[a] = string_removeOuterWhiteSpace(args[a]);
-	}
-	if (string_caseInsensitiveMatch(command, U"Visible")) {
-		parse_shape(state, args, false);
-	} else if (string_caseInsensitiveMatch(command, U"Shadow")) {
-		parse_shape(state, args, true);
-	} else {
-		printText("    Unrecognized command ", command, ".\n");
-	}
-}
-
 static void parse_dsm(ParserState& state, const ReadableString& content) {
 static void parse_dsm(ParserState& state, const ReadableString& content) {
-	List<ReadableString> lines = content.split(U'\n');
+	List<ReadableString> lines = string_split(content, U'\n');
+	List<ReadableString> args; // Reusing the buffer for in-place splitting of arguments on each line
 	for (int l = 0; l < lines.length(); l++) {
 	for (int l = 0; l < lines.length(); l++) {
 		// Get the current line
 		// Get the current line
 		ReadableString line = lines[l];
 		ReadableString line = lines[l];
@@ -758,7 +746,17 @@ static void parse_dsm(ParserState& state, const ReadableString& content) {
 			} else if (colonIndex > -1) {
 			} else if (colonIndex > -1) {
 				ReadableString command = string_removeOuterWhiteSpace(line.before(colonIndex));
 				ReadableString command = string_removeOuterWhiteSpace(line.before(colonIndex));
 				ReadableString argContent = line.after(colonIndex);
 				ReadableString argContent = line.after(colonIndex);
-				parse_command(state, command, argContent);
+				string_split_inPlace(args, argContent, U',');
+				for (int a = 0; a < args.length(); a++) {
+					args[a] = string_removeOuterWhiteSpace(args[a]);
+				}
+				if (string_caseInsensitiveMatch(command, U"Visible")) {
+					parse_shape(state, args, false);
+				} else if (string_caseInsensitiveMatch(command, U"Shadow")) {
+					parse_shape(state, args, true);
+				} else {
+					printText("    Unrecognized command ", command, ".\n");
+				}
 			} else if (blockStartIndex > -1 && blockEndIndex > -1) {
 			} else if (blockStartIndex > -1 && blockEndIndex > -1) {
 				String block = string_removeOuterWhiteSpace(line.inclusiveRange(blockStartIndex + 1, blockEndIndex - 1));
 				String block = string_removeOuterWhiteSpace(line.inclusiveRange(blockStartIndex + 1, blockEndIndex - 1));
 				parse_scope(state, block);
 				parse_scope(state, block);

+ 41 - 0
Source/test/tests/StringTest.cpp

@@ -136,6 +136,47 @@ START_TEST(String)
 	ASSERT_MATCH(dsr::string_combine(1u), U"1");
 	ASSERT_MATCH(dsr::string_combine(1u), U"1");
 	ASSERT_MATCH(dsr::string_combine(14u), U"14");
 	ASSERT_MATCH(dsr::string_combine(14u), U"14");
 	ASSERT_MATCH(dsr::string_combine(135u), U"135");
 	ASSERT_MATCH(dsr::string_combine(135u), U"135");
+	// Number parsing
+	ASSERT_EQUAL(string_toInteger(U"0"), 0);
+	ASSERT_EQUAL(string_toInteger(U"-0"), 0);
+	ASSERT_EQUAL(string_toInteger(U"No digits here."), 0);
+	ASSERT_EQUAL(string_toInteger(U" (12 garbage 34) "), 1234); // You are supposed to catch these errors before converting to an integer
+	ASSERT_EQUAL(string_toInteger(U""), 0);
+	ASSERT_EQUAL(string_toInteger(U"1"), 1);
+	ASSERT_EQUAL(string_toInteger(U"-1"), -1);
+	ASSERT_EQUAL(string_toInteger(U"1024"), 1024);
+	ASSERT_EQUAL(string_toInteger(U"-1024"), -1024);
+	ASSERT_EQUAL(string_toInteger(U"1000000"), 1000000);
+	ASSERT_EQUAL(string_toInteger(U"-1000000"), -1000000);
+	ASSERT_EQUAL(string_toInteger(U"123"), 123);
+	ASSERT_EQUAL(string_toDouble(U"123"), 123.0);
+	ASSERT_EQUAL(string_toDouble(U"123.456"), 123.456);
+	{ // Splitting
+		List<ReadableString> result;
+		string_split_inPlace(result, U"a.b.c.d", U'.');
+		ASSERT_EQUAL(result.length(), 4);
+		ASSERT_MATCH(result[0], U"a");
+		ASSERT_MATCH(result[1], U"b");
+		ASSERT_MATCH(result[2], U"c");
+		ASSERT_MATCH(result[3], U"d");
+		String content = U"One Two Three";
+		result = string_split(content, U' ');
+		ASSERT_EQUAL(result.length(), 3);
+		ASSERT_MATCH(result[0], U"One");
+		ASSERT_MATCH(result[1], U"Two");
+		ASSERT_MATCH(result[2], U"Three");
+		string_split_inPlace(result, U"Four.Five", U'.', true);
+		ASSERT_EQUAL(result.length(), 5);
+		ASSERT_MATCH(result[0], U"One");
+		ASSERT_MATCH(result[1], U"Two");
+		ASSERT_MATCH(result[2], U"Three");
+		ASSERT_MATCH(result[3], U"Four");
+		ASSERT_MATCH(result[4], U"Five");
+		string_split_inPlace(result, U" 1 | 2 ", U'|');
+		ASSERT_EQUAL(result.length(), 2);
+		ASSERT_MATCH(result[0], U" 1 ");
+		ASSERT_MATCH(result[1], U" 2 ");
+	}
 	// TODO: Test taking a part of a parent string with a start offset, leaving the parent scope,
 	// TODO: Test taking a part of a parent string with a start offset, leaving the parent scope,
 	//       and expanding with append while the buffer isn't shared but has an offset from buffer start.
 	//       and expanding with append while the buffer isn't shared but has an offset from buffer start.
 	// TODO: Assert that buffers are shared when they should, but prevents side-effects when one is being written to.
 	// TODO: Assert that buffers are shared when they should, but prevents side-effects when one is being written to.