Permalink
Browse files

More tests and fixes.

  • Loading branch information...
1 parent 12e0385 commit 2d9ce2f369d618c143430e33ead899ef01a14d5f @tabish121 tabish121 committed Nov 7, 2013
@@ -337,7 +337,16 @@ const char* String::c_str() const {
return (const char*) (contents->value.get() + contents->offset);
}
- throw UnsupportedOperationException(__FILE__, __LINE__, "Not yet implemented for offset values");
+ Contents* newContents = new Contents(contents->length);
+
+ System::arraycopy(contents->value.get(), contents->offset,
+ newContents->value.get(), 0, contents->length);
+
+ Contents* oldContents = this->contents;
+ this->contents = newContents;
+ delete oldContents;
+
+ return contents->value.get();
}
////////////////////////////////////////////////////////////////////////////////
@@ -357,6 +366,22 @@ char String::charAt(int index) const {
}
////////////////////////////////////////////////////////////////////////////////
+String String::compact() const {
+
+ // Empty String.
+ if (contents->value.length() == 0) {
+ return *this;
+ }
+
+ // Don't do anything if the string is already compact.
+ if (contents->value.length() > this->contents->length + 1) {
+ return String(contents->value.get(), contents->offset, contents->length);
+ }
+
+ return *this;
+}
+
+////////////////////////////////////////////////////////////////////////////////
int String::compareTo(const String& string) const {
int o1 = contents->offset;
@@ -32,14 +32,30 @@ namespace lang {
class Contents;
/**
- * The String class represents an immutable sequence of chars.
+ * An immutable sequence of characters.
+ *
+ * This class is implemented using a char[]. The length of the array may exceed
+ * the length of the string. For example, the string "Hello" may be backed by
+ * the array {@code ['H', 'e', 'l', 'l', 'o', 'W'. 'o', 'r', 'l', 'd']} with
+ * offset 0 and length 5.
+ *
+ * Multiple strings can share the same char[] because strings are immutable.
+ *
+ * The substring method always returns a string that shares the backing array of
+ * its source string. Generally this is an optimization: fewer character arrays
+ * need to be allocated, and less copying is necessary. But this can also lead
+ * to unwanted heap retention. Taking a short substring of long string means that
+ * the long shared char[] won't be garbage until both strings are destroyed. This
+ * typically happens when parsing small substrings out of a large input. To avoid
+ * this where necessary, call the compact method which allocates a new array that
+ * is just big enough to store the String's content.
*
* @since 1.0
*/
class DECAF_API String: public CharSequence {
private:
- Contents* contents;
+ mutable Contents* contents;
public:
@@ -240,7 +256,11 @@ namespace lang {
/**
* Returns a const char* value to allow easier coexistence with standard c++
- * string operations..
+ * string operations.
+ *
+ * This method can result in a compaction of the String's backing store into a
+ * new character array in order to return a pointer value that is guaranteed to
+ * be NULL terminated.
*
* @returns a const char* value for this String.
*/
@@ -249,6 +269,20 @@ namespace lang {
public:
/**
+ * If the String instance is holding a reference to a character array that is larger
+ * than the string's view of the backing store a new array is allocated and the
+ * characters from the substring this String represents are copied to the new backing
+ * store and returned in the resulting String object.
+ *
+ * This can free up heap memory when a String is holding a large array but only
+ * viewing a small portion of it and the original source String is no longer also
+ * maintaining a reference to the backing store.
+ *
+ * @returns a new String instance with a compacted backing store.
+ */
+ String compact() const;
+
+ /**
* Compares two strings lexicographically. The comparison is based on the value
* of each character in the strings. The character sequence represented by this
* String is compared lexicographically to the character sequence represented by
@@ -83,6 +83,11 @@ void StringTest::testConstructorCString() {
"Should have thrown an IndexOutOfBoundsException",
test.charAt(5),
IndexOutOfBoundsException);
+
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "Should have thrown an NullPointerException",
+ String((const char*)NULL),
+ NullPointerException);
}
////////////////////////////////////////////////////////////////////////////////
@@ -206,6 +211,22 @@ void StringTest::testConstructorString() {
}
////////////////////////////////////////////////////////////////////////////////
+void StringTest::testConstructorCharFill() {
+
+ String expected("AAAAA");
+ String input('A', 5);
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("String fill failed", expected, input);
+
+ CPPUNIT_ASSERT_MESSAGE("String should be empty", String('A', 0).isEmpty());
+
+ CPPUNIT_ASSERT_THROW_MESSAGE(
+ "Should have thrown an IndexOutOfBoundsException",
+ String('A', -1),
+ IndexOutOfBoundsException);
+}
+
+////////////////////////////////////////////////////////////////////////////////
void StringTest::testAssignmentString() {
String transient;
@@ -275,6 +296,26 @@ void StringTest::testAssignmentCString() {
}
////////////////////////////////////////////////////////////////////////////////
+void StringTest::testCompact() {
+
+ const String input("HelloWorld");
+ const String expected("World");
+
+ CPPUNIT_ASSERT_MESSAGE("Incorrect substring returned", expected.equals(input.substring(5)));
+ CPPUNIT_ASSERT_MESSAGE("not identical", expected.substring(0) == expected);
+
+ String subStr = input.substring(5);
+ CPPUNIT_ASSERT_MESSAGE("wrong length returned.", subStr.length() == 5);
+ String compacted = subStr.compact();
+ CPPUNIT_ASSERT_MESSAGE("wrong length returned.", compacted.length() == 5);
+ CPPUNIT_ASSERT_MESSAGE("Incorrect compacted string returned", expected.equals(compacted));
+
+ String empty;
+ empty = empty.compact();
+ CPPUNIT_ASSERT_MESSAGE("wrong length returned.", empty.isEmpty());
+}
+
+////////////////////////////////////////////////////////////////////////////////
void StringTest::testIsEmpty() {
String hw("HelloWorld");
@@ -453,15 +494,22 @@ void StringTest::testCStr() {
String substr = hw.substring(5);
String world = "World";
- CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid string returned", world, substr);
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Invalid string returned",
+ std::string(world.c_str()), std::string(substr.c_str()));
}
////////////////////////////////////////////////////////////////////////////////
void StringTest::testEndsWith() {
const String input("HelloWorld");
- CPPUNIT_ASSERT_MESSAGE("Failed to find ending string", input.endsWith("ld"));
- CPPUNIT_ASSERT_MESSAGE("Failed to not find ending string", !input.endsWith("lo"));
+ CPPUNIT_ASSERT_MESSAGE("Failed to find ending String", input.endsWith(String("ld")));
+ CPPUNIT_ASSERT_MESSAGE("Failed to not find ending String", !input.endsWith(String("lo")));
+
+ CPPUNIT_ASSERT_MESSAGE("Failed to find ending std::string", input.endsWith(std::string("ld")));
+ CPPUNIT_ASSERT_MESSAGE("Failed to not find ending std::string", !input.endsWith(std::string("lo")));
+
+ CPPUNIT_ASSERT_MESSAGE("Failed to find ending C string", input.endsWith("ld"));
+ CPPUNIT_ASSERT_MESSAGE("Failed to not find ending C string", !input.endsWith("lo"));
}
////////////////////////////////////////////////////////////////////////////////
@@ -605,6 +653,8 @@ void StringTest::testLastIndexOfString() {
CPPUNIT_ASSERT_EQUAL_MESSAGE("Returned incorrect index", 5, input.lastIndexOf(String("World")));
CPPUNIT_ASSERT_EQUAL_MESSAGE("Found String outside of index",
-1, input.lastIndexOf(String("HeKKKKKKKK")));
+
+ CPPUNIT_ASSERT_EQUAL_MESSAGE("Returned incorrect index", input.length(), input.lastIndexOf(String()));
}
////////////////////////////////////////////////////////////////////////////////
@@ -34,9 +34,11 @@ namespace lang {
CPPUNIT_TEST( testConstructorCStringSizeOffsetAndLength );
CPPUNIT_TEST( testConstructorStdString );
CPPUNIT_TEST( testConstructorString );
+ CPPUNIT_TEST( testConstructorCharFill );
CPPUNIT_TEST( testAssignmentString );
CPPUNIT_TEST( testAssignmentStdString );
CPPUNIT_TEST( testAssignmentCString );
+ CPPUNIT_TEST( testCompact );
CPPUNIT_TEST( testHashCode );
CPPUNIT_TEST( testIsEmpty );
CPPUNIT_TEST( testSubstring1 );
@@ -109,9 +111,11 @@ namespace lang {
void testConstructorCStringSizeOffsetAndLength();
void testConstructorStdString();
void testConstructorString();
+ void testConstructorCharFill();
void testAssignmentString();
void testAssignmentStdString();
void testAssignmentCString();
+ void testCompact();
void testHashCode();
void testIsEmpty();
void testSubstring1();
@@ -265,8 +265,8 @@
//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::lang::PointerTest );
//#include <decaf/lang/ArrayPointerTest.h>
//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::lang::ArrayPointerTest );
-//#include <decaf/lang/StringTest.h>
-//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::lang::StringTest );
+#include <decaf/lang/StringTest.h>
+CPPUNIT_TEST_SUITE_REGISTRATION( decaf::lang::StringTest );
//
//#include <decaf/net/InetAddressTest.h>
//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::net::InetAddressTest );
@@ -282,8 +282,8 @@
//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::net::SocketTest );
//#include <decaf/net/URITest.h>
//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::net::URITest );
-#include <decaf/net/URLTest.h>
-CPPUNIT_TEST_SUITE_REGISTRATION( decaf::net::URLTest );
+//#include <decaf/net/URLTest.h>
+//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::net::URLTest );
//#include <decaf/net/URISyntaxExceptionTest.h>
//CPPUNIT_TEST_SUITE_REGISTRATION( decaf::net::URISyntaxExceptionTest );
//#include <decaf/net/URLEncoderTest.h>

0 comments on commit 2d9ce2f

Please sign in to comment.