VirtualBox

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

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

IPRT/rest: Early support for polymorphic data objects in the data model. bugref:9167 ..\..\ e:\vbox\svn\trunk\include\iprt\

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