"R-package/tests/vscode:/vscode.git/clone" did not exist on "fee6f4a2283247247510dae9579c2504d9703957"
StringArray.hpp 3.95 KB
Newer Older
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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
/*!
 * Copyright (c) 2020 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
 */
#ifndef __STRING_ARRAY_H__
#define __STRING_ARRAY_H__

#include <new>
#include <vector>
#include <algorithm>

/**
 * Container that manages an array of fixed-length strings.
 *
 * To be compatible with SWIG's `various.i` extension module,
 * the array of pointers to char* must be NULL-terminated:
 *   [char*, char*, char*, ..., NULL]
 * This implies that the length of this array is bigger
 * by 1 element than the number of char* it stores.
 * I.e., _num_elements == _array.size()-1
 *
 * The class also takes care of allocation of the underlying
 * char* memory.
 */
class StringArray
{
  public:
    StringArray(size_t num_elements, size_t string_size)
      : _string_size(string_size),
        _array(num_elements + 1, nullptr)
    {
        _allocate_strings(num_elements, string_size);
    }

    ~StringArray()
    {
        _release_strings();
    }

    /**
     * Returns the pointer to the raw array.
     * Notice its size is greater than the number of stored strings by 1.
     *
     * @return char** pointer to raw data (null-terminated).
     */
    char **data() noexcept
    {
        return _array.data();
    }

    /**
     * Return char* from the array of size _string_size+1.
     * Notice the last element in _array is already
     * considered out of bounds.
     *
     * @param index Index of the element to retrieve.
     * @return pointer or nullptr if index is out of bounds.
     */
    char *getitem(size_t index) noexcept
    {
        if (_in_bounds(index))
            return _array[index];
        else
            return nullptr;
    }

    /**
     * Safely copies the full content data
     * into one of the strings in the array.
     * If that is not possible, returns error (-1).
     *
     * @param index index of the string in the array.
     * @param content content to store
     *
     * @return In case index results in out of bounds access,
     * or content + 1 (null-terminator byte) doesn't fit
     * into the target string (_string_size), it errors out
     * and returns -1.
     */
    int setitem(size_t index, std::string content) noexcept
    {
        if (_in_bounds(index) && content.size() < _string_size)
        {
            std::strcpy(_array[index], content.c_str());
            return 0;
        } else {
            return -1;
        }
    }

    /**
     * @return number of stored strings.
     */
    size_t get_num_elements() noexcept
    {
        return _array.size() - 1;
    }

  private:

    /**
     * Returns true if and only if within bounds.
     * Notice that it excludes the last element of _array (NULL).
     *
     * @param index index of the element
     * @return bool true if within bounds
     */
    bool _in_bounds(size_t index) noexcept
    {
        return index < get_num_elements();
    }

    /**
     * Allocate an array of fixed-length strings.
     *
     * Since a NULL-terminated array is required by SWIG's `various.i`,
     * the size of the array is actually `num_elements + 1` but only
     * num_elements are filled.
     *
     * @param num_elements Number of strings to store in the array.
     * @param string_size The size of each string in the array.
     */
    void _allocate_strings(int num_elements, int string_size)
    {
        for (int i = 0; i < num_elements; ++i)
        {
            // Leave space for \0 terminator:
            _array[i] = new (std::nothrow) char[string_size + 1];

            // Check memory allocation:
            if (! _array[i]) {
                _release_strings();
                throw std::bad_alloc();
            }
        }
    }

    /**
     * Deletes the allocated strings.
     */
    void _release_strings() noexcept
    {
        std::for_each(_array.begin(), _array.end(), [](char* c) { delete[] c; });
    }

    const size_t _string_size;
    std::vector<char*> _array;
};

#endif // __STRING_ARRAY_H__