# How absl::Cord Handles Memory Allocation: When to Choose It Over std::string

> Discover how absl::Cord's chunked memory allocation provides O(1) appends and zero-copy sharing. Learn when to choose Cord over std::string for large strings and complex operations.

- Repository: [Abseil/abseil-cpp](https://github.com/abseil/abseil-cpp)
- Tags: deep-dive
- Published: 2026-07-11

---

**`absl::Cord` uses a chunked, reference-counted tree structure for strings larger than 15 bytes, enabling O(1) appends, zero-copy sharing, and efficient sub-string operations, while `std::string` remains optimal for small, random-access-heavy workloads.**

The `absl::Cord` class in the [abseil/abseil-cpp](https://github.com/abseil/abseil-cpp) repository reimplements string storage to optimize for large, mutable, and shareable data. Unlike `std::string`'s contiguous buffer, Cord employs a sophisticated node-based architecture that minimizes memory copies during modification and enables cheap data sharing across API boundaries.

## Memory Allocation Architecture of absl::Cord

`absl::Cord` avoids the reallocation penalties of contiguous storage by splitting data into reference-counted chunks managed through a tree of `CordRep` nodes. This design trades occasional pointer indirection for significantly better performance on large string operations.

### Small-Size Optimization (Inline Storage)

For strings of **15 bytes or fewer**, `absl::Cord` stores data directly inside the object itself without heap allocation. In [`absl/strings/cord.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/strings/cord.h) at lines 46-48, the `InlineRep` class defines `kMaxInline` as 15 bytes, using `InlineRep::set_data` to copy bytes directly into the Cord object's internal buffer. This optimization ensures that small strings incur zero allocation overhead and remain cache-friendly, similar to `std::string` short-string optimizations.

### Chunk-Based Tree Structure for Large Data

When construction exceeds the inline threshold, `absl::Cord` allocates a tree of nodes via `NewTree` (found in `absl/strings/cord.cc` at lines 111-117). The implementation selects between two node types based on size:

- **Flat nodes** (`CordRepFlat`): Allocated via `CreateFlat` for moderate strings up to approximately 2 KB, stored as single contiguous blocks.
- **B-tree nodes** (`CordRepBtree`): Created via `NewBtree` (lines 99-108) for very large strings, organizing data into a balanced tree that supports logarithmic-time prepend and append operations for multi-megabyte payloads.

This chunking strategy means appending to a 100 MiB Cord does not require moving or copying existing data—only allocating a new node and updating tree pointers.

### Reference-Counted Sharing

Every `CordRep` node inherits from `CordRepBase`, which contains an atomic reference count. When copying a Cord, the implementation calls `CordRep::Ref` to increment the count, making copies **O(1)** regardless of data size. The `CordRep::Unref` method handles decrementing counts and deallocation when the last reference disappears. This sharing mechanism enables efficient passing of large strings by value across threads and function boundaries without the copy penalties associated with `std::string`.

### Zero-Copy External Buffers

For ingesting memory-mapped files or network buffers without copying, `absl::Cord` provides `MakeCordFromExternal` (implemented in `absl/strings/cord.cc` at lines 165-173). This function creates a `CordRepExternal` node that points to user-supplied memory and registers a releaser callback. The external buffer remains owned by the Cord until the last reference drops, eliminating the need to duplicate large incoming data into Cord's internal storage.

### Append Optimization with Spare Capacity

To minimize allocations during incremental growth, `absl::Cord` tracks spare capacity in leaf nodes. The `PrepareAppendRegion` method (lines 236-263 in `cord.cc`) checks if the last chunk has unused space; if so, new data writes directly into that existing allocation. When no space exists, `Cord::AppendImpl` adds a new tree node via `InlineRep::AppendTree`. This strategy amortizes allocation costs during repeated append operations.

## When to Prefer absl::Cord Over std::string

Choose `absl::Cord` when your workload matches these patterns, otherwise prefer `std::string` for simplicity.

**Prefer `absl::Cord` for:**

- **Large strings with frequent prepend/append**: Adding data to either end is amortized O(1) because new chunks attach without moving existing data, unlike `std::string`'s linear reallocation cost.
- **Sharing immutable data across boundaries**: Copying is O(1) via reference counting, and sub-cords share underlying nodes.
- **Zero-copy ingestion**: Use `MakeCordFromExternal` to wrap existing buffers without duplication.
- **Frequent slicing operations**: `Subcord` creates lightweight views (lines 708-746 in `cord.cc`) that reference original chunks, avoiding the full copies required by `std::string::substr`.

**Prefer `std::string` for:**

- **Small strings under 16 bytes**: Cord incurs extra indirection overhead; `std::string` is more compact and faster for tiny data.
- **Heavy random access**: Cord's `operator[]` requires O(log N) traversal (lines 1510-1564 in [`cord.h`](https://github.com/abseil/abseil-cpp/blob/main/cord.h)), while `std::string` provides O(1) indexing.
- **Guaranteed contiguous storage**: Cord stores data in chunks; only `Flatten` (lines 1401-1412 in `cord.cc`) creates a contiguous buffer, which copies the entire payload.

## Practical Implementation Examples

```cpp
#include "absl/strings/cord.h"
#include <iostream>

// 1. Small data stays inline (no heap allocation)
absl::Cord small("hello");  // Stored in InlineRep (≤15 bytes)

// 2. Large data automatically uses tree nodes
std::string huge(5'000'000, 'x');  // 5 MiB of 'x'
absl::Cord large(huge);           // Creates B-tree of chunks via NewTree

// 3. O(1) prepend/append without reallocation
large.Prepend("header-");  // Adds new chunk at front
large.Append("-footer"); // Adds new chunk at back

// 4. Zero-copy external buffer ownership
char* buffer = new char[1024];
std::memcpy(buffer, "network data", 12);
absl::Cord external = absl::MakeCordFromExternal(
    absl::string_view(buffer, 1024),
    [](absl::string_view sv) { delete[] sv.data(); });

// 5. Lightweight sub-cord sharing underlying chunks
absl::Cord view = large.Subcord(7, 10);  // 10-byte view, O(1)

// 6. Flatten to std::string only when contiguous memory required
std::string flat = std::string(large);  // Copies all chunks once

```

## Summary

- **`absl::Cord` stores data in reference-counted chunks** (flat nodes or B-trees) rather than contiguous buffers, enabling O(1) copies and appends.
- **Strings ≤15 bytes use inline storage** in [`absl/strings/cord.h`](https://github.com/abseil/abseil-cpp/blob/main/absl/strings/cord.h), avoiding heap allocation for small payloads.
- **`MakeCordFromExternal`** (lines 165-173 in `cord.cc`) enables zero-copy ingestion of external memory.
- **Prefer Cord for large, shared, or frequently modified strings**; use `std::string` for small data or when guaranteed contiguous memory and O(1) indexing are required.
- **Call `Flatten`** (lines 1401-1412) to convert to contiguous storage when needed, accepting the cost of a full copy.

## Frequently Asked Questions

### What is the memory overhead of absl::Cord compared to std::string?

`absl::Cord` carries a per-object overhead of approximately 16 bytes for the inline representation plus pointer indirection for heap-allocated nodes. For strings under 15 bytes, this makes Cord slightly larger than `std::string`. However, for strings above 1 KB, Cord's chunk-based approach typically reduces overall memory pressure because it avoids reallocation waste and enables sharing through reference counting, whereas `std::string` copies entire buffers on assignment.

### How do I convert absl::Cord to std::string?

Use the explicit cast operator or assignment: `std::string s = std::string(cord);` or `std::string s(cord);`. This calls `Cord::Flatten` internally, which copies all chunks into a single contiguous allocation. Note that this operation is O(N) where N is the string length, unlike Cord's O(1) copy constructor. If you only need temporary contiguous access, consider `cord.TryFlat()` which returns a `string_view` without copying if the data happens to be contiguous.

### Is absl::Cord thread-safe?

Yes, `absl::Cord` is thread-safe for concurrent read operations because its `CordRep` nodes use atomic reference counting. Multiple threads can copy, read, or create sub-cords from the same Cord instance safely without synchronization. However, mutable operations (such as `Append` or `Prepend`) are not thread-safe and require external synchronization if the same Cord instance is modified concurrently from multiple threads.

### When should I avoid absl::Cord?

Avoid `absl::Cord` when you require frequent random access by index, as `operator[]` traverses the chunk tree in O(log N) time compared to `std::string`'s O(1). Also avoid it for small, short-lived strings where the inline optimization of `std::string` outperforms Cord's extra indirection, or when you need guaranteed C-style contiguous buffers without the copying penalty of `Flatten`.