VirtualBox

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

Last change on this file since 78402 was 76553, checked in by vboxsync, 6 years ago

scm --update-copyright-year

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette