VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/rest/RTCRestArrayBase.cpp

Last change on this file was 98103, checked in by vboxsync, 16 months ago

Copyright year updates by scm.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.2 KB
Line 
1/* $Id: RTCRestArrayBase.cpp 98103 2023-01-17 14:15:46Z vboxsync $ */
2/** @file
3 * IPRT - C++ REST, RTCRestArrayBase implementation.
4 */
5
6/*
7 * Copyright (C) 2018-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * The contents of this file may alternatively be used under the terms
26 * of the Common Development and Distribution License Version 1.0
27 * (CDDL), a copy of it is provided in the "COPYING.CDDL" file included
28 * in the VirtualBox distribution, in which case the provisions of the
29 * CDDL are applicable instead of those of the GPL.
30 *
31 * You may elect to license modified versions of this file under the
32 * terms and conditions of either the GPL or the CDDL or both.
33 *
34 * SPDX-License-Identifier: GPL-3.0-only OR CDDL-1.0
35 */
36
37
38/*********************************************************************************************************************************
39* Header Files *
40*********************************************************************************************************************************/
41#define LOG_GROUP RTLOGGROUP_REST
42#include <iprt/cpp/restarray.h>
43
44#include <iprt/err.h>
45#include <iprt/string.h>
46#include <iprt/cpp/restoutput.h>
47
48
49/*********************************************************************************************************************************
50* Global Variables *
51*********************************************************************************************************************************/
52/** Separator characters. */
53static char const g_szSep[RTCRestObjectBase::kCollectionFormat_Mask + 1] = ",, \t|,,";
54
55
56/**
57 * Default destructor.
58 */
59RTCRestArrayBase::RTCRestArrayBase() RT_NOEXCEPT
60 : RTCRestObjectBase()
61 , m_papElements(NULL)
62 , m_cElements(0)
63 , m_cCapacity(0)
64{
65}
66
67
68#if 0 /* should not be used */
69/**
70 * Copy constructor.
71 */
72RTCRestArrayBase::RTCRestArrayBase(RTCRestArrayBase const &a_rThat);
73#endif
74
75/**
76 * Destructor.
77 */
78RTCRestArrayBase::~RTCRestArrayBase()
79{
80 clear();
81
82 if (m_papElements)
83 {
84 RTMemFree(m_papElements);
85 m_papElements = NULL;
86 m_cCapacity = 0;
87 }
88}
89
90
91#if 0 /* should not be used */
92/**
93 * Copy assignment operator.
94 */
95RTCRestArrayBase &RTCRestArrayBase::operator=(RTCRestArrayBase const &a_rThat);
96#endif
97
98
99/*********************************************************************************************************************************
100* Overridden methods *
101*********************************************************************************************************************************/
102
103RTCRestObjectBase *RTCRestArrayBase::baseClone() const RT_NOEXCEPT
104{
105 RTCRestArrayBase *pClone = createClone();
106 if (pClone)
107 {
108 int rc = pClone->copyArrayWorkerNoThrow(*this);
109 if (RT_SUCCESS(rc))
110 return pClone;
111 delete pClone;
112 }
113 return NULL;
114}
115
116
117int RTCRestArrayBase::resetToDefault() RT_NOEXCEPT
118{
119 /* The default state of an array is empty. At least for now. */
120 clear();
121 m_fNullIndicator = false;
122 return VINF_SUCCESS;
123}
124
125
126RTCRestOutputBase &RTCRestArrayBase::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
127{
128 if (!m_fNullIndicator)
129 {
130 uint32_t const uOldState = a_rDst.beginArray();
131 for (size_t i = 0; i < m_cElements; i++)
132 {
133 a_rDst.valueSeparator();
134 m_papElements[i]->serializeAsJson(a_rDst);
135 }
136 a_rDst.endArray(uOldState);
137 }
138 else
139 a_rDst.nullValue();
140 return a_rDst;
141}
142
143
144int RTCRestArrayBase::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
145{
146 /*
147 * Make sure the object starts out with an empty map.
148 */
149 if (m_cElements > 0)
150 clear();
151 m_fNullIndicator = false;
152
153 /*
154 * Iterate the array values.
155 */
156 RTJSONIT hIterator;
157 int rcRet = RTJsonIteratorBeginArray(a_rCursor.m_hValue, &hIterator);
158 if (RT_SUCCESS(rcRet))
159 {
160 for (size_t idxName = 0;; idxName++)
161 {
162 /* Setup sub-cursor. */
163 RTCRestJsonCursor SubCursor(a_rCursor);
164 int rc = RTJsonIteratorQueryValue(hIterator, &SubCursor.m_hValue, &SubCursor.m_pszName);
165 if (RT_SUCCESS(rc))
166 {
167 char szName[32];
168 RTStrPrintf(szName, sizeof(szName), "[%u]", idxName);
169 SubCursor.m_pszName = szName;
170
171 /* Call the static deserializeInstanceFromJson method of the value class. */
172 RTCRestObjectBase *pObj = NULL;
173 rc = deserializeValueInstanceFromJson(SubCursor, &pObj);
174 if (RT_SUCCESS(rc))
175 Assert(pObj);
176 else if (RT_SUCCESS(rcRet))
177 rcRet = rc;
178 if (pObj)
179 {
180 rc = insertWorker(~(size_t)0, pObj, false /*a_fReplace*/);
181 if (RT_SUCCESS(rc))
182 { /* likely */ }
183 else
184 {
185 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "Array insert failed (index %zu): %Rrc",
186 idxName, rc);
187 delete pObj;
188 }
189 }
190 }
191 else
192 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorQueryValue failed: %Rrc", rc);
193
194 /*
195 * Advance.
196 */
197 rc = RTJsonIteratorNext(hIterator);
198 if (RT_SUCCESS(rc))
199 { /* likely */ }
200 else if (rc == VERR_JSON_ITERATOR_END)
201 break;
202 else
203 {
204 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorNext failed: %Rrc", rc);
205 break;
206 }
207 }
208
209 RTJsonIteratorFree(hIterator);
210 }
211 else if (rcRet == VERR_JSON_IS_EMPTY)
212 rcRet = VINF_SUCCESS;
213 else if ( rcRet == VERR_JSON_VALUE_INVALID_TYPE
214 && RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
215 {
216 m_fNullIndicator = true;
217 rcRet = VINF_SUCCESS;
218 }
219 else
220 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rcRet,
221 "RTJsonIteratorBeginrray failed: %Rrc (type %s)",
222 rcRet, RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
223 return rcRet;
224
225}
226
227
228int RTCRestArrayBase::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const RT_NOEXCEPT
229{
230 int rc;
231 if (!m_fNullIndicator)
232 {
233 if (m_cElements)
234 {
235 char const chSep = g_szSep[a_fFlags & kCollectionFormat_Mask];
236
237 rc = m_papElements[0]->toString(a_pDst, a_fFlags);
238 for (size_t i = 1; RT_SUCCESS(rc) && i < m_cElements; i++)
239 {
240 rc = a_pDst->appendNoThrow(chSep);
241 if (RT_SUCCESS(rc))
242 rc = m_papElements[i]->toString(a_pDst, a_fFlags | kToString_Append);
243 }
244 }
245 else
246 {
247 if (!(a_fFlags & kToString_Append))
248 a_pDst->setNull();
249 rc = VINF_SUCCESS;
250 }
251 }
252 else if (a_fFlags & kToString_Append)
253 rc = a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
254 else
255 rc = a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
256
257 return rc;
258}
259
260
261int RTCRestArrayBase::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
262 uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) RT_NOEXCEPT
263{
264 /*
265 * Clear the array. If the string is empty, we have an empty array and is done.
266 */
267 if (!(a_fFlags & kToString_Append))
268 clear();
269 if (a_rValue.isEmpty())
270 return VINF_SUCCESS;
271
272 /*
273 * Look for a separator so we don't mistake a initial null element for a null array.
274 */
275 char const chSep = g_szSep[a_fFlags & kCollectionFormat_Mask];
276 size_t offSep = a_rValue.find(chSep);
277 if ( offSep != RTCString::npos
278 || !a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
279 {
280 RTCString strTmp;
281 size_t offStart = 0;
282 int rcRet = VINF_SUCCESS;
283 for (;;)
284 {
285 /* Copy the element value into its own string buffer. */
286 int rc = strTmp.assignNoThrow(a_rValue, offStart, (offSep == RTCString::npos ? a_rValue.length() : offSep) - offStart);
287 AssertRCReturn(rc, rc);
288
289 /* Create a new element, insert it and pass it the value string. */
290 RTCRestObjectBase *pObj = createValue();
291 AssertPtrReturn(pObj, VERR_NO_MEMORY);
292
293 rc = insertWorker(~(size_t)0, pObj, false);
294 AssertRCReturnStmt(rc, delete pObj, rc);
295
296 char szName[128];
297 RTStrPrintf(szName, sizeof(szName), "%.*s[%zu]", 116, a_pszName ? a_pszName : "", size());
298 rc = pObj->fromString(strTmp, a_pszName, a_pErrInfo, 0);
299 if (RT_SUCCESS(rc))
300 { /* likely */ }
301 else if (RT_SUCCESS(rcRet))
302 rcRet = rc;
303
304 /*
305 * Done? Otherwise advance.
306 */
307 if (offSep == RTCString::npos)
308 break;
309 offStart = offSep + 1;
310 offSep = a_rValue.find(chSep, offStart);
311 }
312 return rcRet;
313 }
314
315 /*
316 * Consider this a null array even if it could also be an array with a single
317 * null element. This is just an artifact of an imperfect serialization format.
318 */
319 setNull();
320 return VINF_SUCCESS;
321}
322
323
324RTCRestObjectBase::kTypeClass RTCRestArrayBase::typeClass(void) const RT_NOEXCEPT
325{
326 return kTypeClass_Array;
327}
328
329
330const char *RTCRestArrayBase::typeName(void) const RT_NOEXCEPT
331{
332 return "RTCRestArray<ElementType>";
333}
334
335
336
337/*********************************************************************************************************************************
338* Array methods *
339*********************************************************************************************************************************/
340
341void RTCRestArrayBase::clear() RT_NOEXCEPT
342{
343 size_t i = m_cElements;
344 while (i-- > 0)
345 {
346 delete m_papElements[i];
347 m_papElements[i] = NULL;
348 }
349 m_cElements = 0;
350 m_fNullIndicator = false;
351}
352
353
354bool RTCRestArrayBase::removeAt(size_t a_idx) RT_NOEXCEPT
355{
356 if (a_idx == ~(size_t)0)
357 a_idx = m_cElements - 1;
358 if (a_idx < m_cElements)
359 {
360 delete m_papElements[a_idx];
361 m_papElements[a_idx] = NULL;
362
363 m_cElements--;
364 if (a_idx < m_cElements)
365 memmove(&m_papElements[a_idx], &m_papElements[a_idx + 1], (m_cElements - a_idx) * sizeof(m_papElements[0]));
366 }
367 return false;
368}
369
370
371int RTCRestArrayBase::ensureCapacity(size_t a_cEnsureCapacity) RT_NOEXCEPT
372{
373 if (m_cCapacity < a_cEnsureCapacity)
374 {
375 if (a_cEnsureCapacity < 512)
376 a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 16);
377 else if (a_cEnsureCapacity < 16384)
378 a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 128);
379 else
380 a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 512);
381
382 void *pvNew = RTMemRealloc(m_papElements, sizeof(m_papElements[0]) * a_cEnsureCapacity);
383 if (pvNew)
384 {
385 m_papElements = (RTCRestObjectBase **)pvNew;
386 memset(&m_papElements[m_cCapacity], 0, (a_cEnsureCapacity - m_cCapacity) * sizeof(sizeof(m_papElements[0])));
387 m_cCapacity = a_cEnsureCapacity;
388 }
389 else
390 return VERR_NO_MEMORY;
391 }
392 return VINF_SUCCESS;
393}
394
395
396int RTCRestArrayBase::copyArrayWorkerNoThrow(RTCRestArrayBase const &a_rThat) RT_NOEXCEPT
397{
398 int rc;
399 clear();
400 if (a_rThat.m_cElements == 0)
401 {
402 m_fNullIndicator = a_rThat.m_fNullIndicator;
403 rc = VINF_SUCCESS;
404 }
405 else
406 {
407 Assert(!a_rThat.m_fNullIndicator);
408 rc = ensureCapacity(a_rThat.m_cElements);
409 if (RT_SUCCESS(rc))
410 {
411 for (size_t i = 0; i < a_rThat.m_cElements; i++)
412 {
413 AssertPtr(a_rThat.m_papElements[i]);
414 rc = insertCopyWorker(i, *a_rThat.m_papElements[i], false);
415 if (RT_SUCCESS(rc))
416 { /* likely */ }
417 else
418 return rc;
419 }
420 }
421 }
422 return rc;
423}
424
425void RTCRestArrayBase::copyArrayWorkerMayThrow(RTCRestArrayBase const &a_rThat)
426{
427 int rc = copyArrayWorkerNoThrow(a_rThat);
428 if (RT_SUCCESS(rc))
429 return;
430 throw std::bad_alloc();
431}
432
433
434int RTCRestArrayBase::insertWorker(size_t a_idx, RTCRestObjectBase *a_pValue, bool a_fReplace) RT_NOEXCEPT
435{
436 AssertPtrReturn(a_pValue, VERR_INVALID_POINTER);
437
438 if (a_idx == ~(size_t)0)
439 a_idx = m_cElements;
440
441 if (a_idx <= m_cElements)
442 {
443 if (a_idx == m_cElements || !a_fReplace)
444 {
445 /* Make sure we've got array space. */
446 if (m_cElements + 1 < m_cCapacity)
447 { /* kind of likely */ }
448 else
449 {
450 int rc = ensureCapacity(m_cElements + 1);
451 if (RT_SUCCESS(rc))
452 { /* likely */ }
453 else
454 return rc;
455 }
456
457 /* Shift following elements before inserting. */
458 if (a_idx < m_cElements)
459 memmove(&m_papElements[a_idx + 1], &m_papElements[a_idx], (m_cElements - a_idx) * sizeof(m_papElements[0]));
460 m_papElements[a_idx] = a_pValue;
461 m_cElements++;
462#ifdef RT_STRICT
463 for (size_t i = 0; i < m_cElements; i++)
464 AssertPtr(m_papElements[i]);
465#endif
466 m_fNullIndicator = false;
467 return VINF_SUCCESS;
468 }
469
470 /* Replace element. */
471 delete m_papElements[a_idx];
472 m_papElements[a_idx] = a_pValue;
473 m_fNullIndicator = false;
474 return VWRN_ALREADY_EXISTS;
475 }
476 return VERR_OUT_OF_RANGE;
477}
478
479
480int RTCRestArrayBase::insertCopyWorker(size_t a_idx, RTCRestObjectBase const &a_rValue, bool a_fReplace) RT_NOEXCEPT
481{
482 int rc;
483 RTCRestObjectBase *pValueCopy = a_rValue.baseClone();
484 if (pValueCopy)
485 {
486 rc = insertWorker(a_idx, pValueCopy, a_fReplace);
487 if (RT_SUCCESS(rc))
488 { /* likely */ }
489 else
490 delete pValueCopy;
491 }
492 else
493 rc = VERR_NO_MEMORY;
494 return rc;
495}
496
Note: See TracBrowser for help on using the repository browser.

© 2023 Oracle
ContactPrivacy policyTerms of Use