Skip to content

Custom String

BrandonRobare edited this page Jun 2, 2026 · 1 revision

Custom String

A dynamic string class built on a raw char* that I manage myself. The point of the project was to write the memory handling by hand: allocate, copy, free, and get the copy semantics right without leaking. There is also an application, logview, that uses the class to parse web server logs.

How it stores a string

Each String holds a char* str and an int stringSize that counts the buffer length including the null terminator. The class invariant is that str[length()] == 0 and capacity() == stringSize - 1. Because the buffer is heap-allocated, the class needs its own copy constructor, destructor, and assignment, which is the whole reason this kind of class shows up in a data-structures course.

Assignment uses copy-and-swap: operator= takes its argument by value (so the copy constructor runs), then swaps the guts of that temporary with *this. The temporary's destructor frees the old buffer. It is short and exception-safe.

Class

classDiagram
    class String {
        -char* str
        -int stringSize
        +String()
        +String(char)
        +String(const char[])
        +String(const String&)
        +~String()
        +swap(String&) void
        +operator=(String) String&
        +operator+=(const String&) String&
        +operator==(const String&) bool
        +operator-less-than(const String&) bool
        +operator[](int) char&
        +length() int
        +capacity() int
        +substr(int, int) String
        +findch(int, char) int
        +findstr(int, const String&) int
        +split(char) vector~String~
        +to_int() int
    }
Loading

Key methods

  • operator+=. Allocates a new buffer sized to both strings, copies the left then the right, frees the old buffer, and repoints str. Free operator+ is built on top of it by value.
  • operator== and operator<. Equality checks length then characters. Less-than walks both strings to the first difference and compares there, which is lexicographic order. The header also declares the rest of the relational operators (<=, !=, >=, >) as free functions defined in terms of those two.
  • substr(start, end). Returns the characters in a range, clamping the bounds so an out-of-range request does not crash.
  • findch and findstr. Linear search for a character or a substring starting at a position, returning the index or -1.
  • split(delim). Returns a std::vector<String> of the pieces between a delimiter. This is what the log parser leans on.
  • to_int. Converts a numeric string to an int by place value. The log parser uses it for byte counts and timestamps.

The header for this class was provided by the instructor so the course test oracles would link against a fixed interface. The implementation in string.cpp is mine.

The logview application

logview reads an Apache-style access log and reports on it. Run it with a mode and a file:

cd string
make logview
./logview all   log_2_small.txt   # print every parsed field
./logview bytes log_2_small.txt   # total bytes served
./logview host  log_2_small.txt   # list the host of each request

logentry.cpp does the parsing. It splits a log line on spaces, then splits the timestamp field again on / and : to pull out day, month, year, hour, minute, and second. A LogEntry holds the host, a Date, a Time, the request, the status, and the byte count. The free functions parse, output_all, by_host, and byte_count drive the three report modes. All of the string slicing goes through my split, substr, and to_int, so the application is really a test of the class under load.

Build and run

cd string
make tests       # build and run the assertion suite
make logview     # build the log application

Under CI the class and tests build with g++ -std=c++11.

Notes and limits

operator+= reallocates on every call, so heavy concatenation in a loop is quadratic. For the log files here that is fine, but a real string would keep spare capacity and double it on growth. The test_generic_* files in this folder are the course's blank test templates (they contain placeholder tokens), not working tests, so CI skips them.

Clone this wiki locally