summaryrefslogtreecommitdiff
path: root/tagstats/string_store.hpp
blob: fad90858b527b7eec7e94a18fce9299ccc292f7d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
#ifndef STRING_STORE_HPP
#define STRING_STORE_HPP

/*

  Copyright 2012 Jochen Topf <jochen@topf.org>.

  This file is part of Tagstats.

  Tagstats is free software: you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published by
  the Free Software Foundation, either version 3 of the License, or
  (at your option) any later version.

  Tagstats is distributed in the hope that it will be useful,
  but WITHOUT ANY WARRANTY; without even the implied warranty of
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  GNU General Public License for more details.

  You should have received a copy of the GNU General Public License
  along with Tagstats.  If not, see <http://www.gnu.org/licenses/>.

*/

#include <list>
#include <stdexcept>
#include <new>
#include <cstring>

/**
 * class StringStore
 *
 * Storage of lots of strings (const char *). Memory is allocated in chunks.
 * If a string is added and there is no space in the current chunk, a new
 * chunk will be allocated.
 *
 * All memory is released when the destructor is called. There is no other way
 * to release all or part of the memory.
 *
 */
class StringStore {

    int chunk_size;

    // number of bytes that are available in the current chunk
    int current_rest_length;

    // pointer where the next string is stored
    char *current_ptr;

    // list of chunks
    std::list<void *> chunks;

    const char *_add_chunk() {
        current_ptr = (char *) malloc(chunk_size);
        if (! current_ptr) {
            throw std::bad_alloc();
        }
        current_rest_length = chunk_size;
        chunks.push_back(current_ptr);
        return current_ptr;
    }

    bool _add(const char *string) {
        if (current_rest_length <= 1) {
            _add_chunk();
        }
        char *next_ptr = (char *) memccpy(current_ptr, string, 0, current_rest_length);
        if (next_ptr) {
            current_rest_length -= (next_ptr - current_ptr);
            current_ptr = next_ptr;
            return true;
        }
        return false;
    }

public:

    StringStore(int chunk_size) : chunk_size(chunk_size) {
        _add_chunk();
    }

    ~StringStore() {
        while (! chunks.empty()) {
            free(chunks.back());
            chunks.pop_back();
        }
    }

    /**
     * Add a null terminated string to the store. This will
     * automatically get more memory if we are out.
     * Returns a pointer to the copy of the string we have
     * allocated.
     *
     * Throws std::length_error if the string we want to
     * add is longer then the chunk size.
     */
    const char *add(const char *string) {
        const char *string_ptr = current_ptr;

        if (! _add(string)) {
            string_ptr = _add_chunk();
            if (! _add(string)) {
                throw std::length_error("strings added to StringStore must be shorter than chunk_size");
            }
        }

        return string_ptr;
    }

    // These functions get you some idea how much memory was
    // used.
    int get_chunk_size() const {
        return chunk_size;
    }

    int get_chunk_count() const {
        return chunks.size();
    }

    int get_used_bytes_in_last_chunk() const {
        return chunk_size - current_rest_length;
    }

};

#endif // STRING_STORE_HPP