StringArray.hpp 4.01 KB
Newer Older
1
2
3
/*!
 * Copyright (c) 2020 Microsoft Corporation. All rights reserved.
 * Licensed under the MIT License. See LICENSE file in the project root for license information.
4
5
 *
 * Author: Alberto Ferreira
6
 */
7
8
#ifndef LIGHTGBM_SWIG_STRINGARRAY_HPP_
#define LIGHTGBM_SWIG_STRINGARRAY_HPP_
9

10
#include <algorithm>
11
#include <new>
12
#include <string>
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <vector>

/**
 * 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.
 */
28
29
class StringArray {
 public:
30
31
    StringArray(size_t num_elements, size_t string_size)
      : _string_size(string_size),
32
        _array(num_elements + 1, nullptr) {
33
34
35
        _allocate_strings(num_elements, string_size);
    }

36
    ~StringArray() {
37
38
39
40
41
42
43
44
45
        _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).
     */
46
    char **data() noexcept {
47
48
49
50
51
52
53
54
55
56
57
        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.
     */
58
    char *getitem(size_t index) noexcept {
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
        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.
     */
78
79
80
    int setitem(size_t index, const std::string &content) noexcept {
        if (_in_bounds(index) && content.size() < _string_size) {
            std::strcpy(_array[index], content.c_str());  // NOLINT
81
82
83
84
85
86
87
88
89
            return 0;
        } else {
            return -1;
        }
    }

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

94
 private:
95
96
97
98
99
100
101
    /**
     * 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
     */
102
    bool _in_bounds(size_t index) noexcept {
103
104
105
106
107
108
109
110
111
112
113
114
115
        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.
     */
116
117
    void _allocate_strings(size_t num_elements, size_t string_size) {
        for (size_t i = 0; i < num_elements; ++i) {
118
119
120
121
            // Leave space for \0 terminator:
            _array[i] = new (std::nothrow) char[string_size + 1];

            // Check memory allocation:
122
            if (!_array[i]) {
123
124
125
126
127
128
129
130
131
                _release_strings();
                throw std::bad_alloc();
            }
        }
    }

    /**
     * Deletes the allocated strings.
     */
132
    void _release_strings() noexcept {
133
134
135
136
137
138
139
        std::for_each(_array.begin(), _array.end(), [](char* c) { delete[] c; });
    }

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

140
#endif  // LIGHTGBM_SWIG_STRINGARRAY_HPP_