VirtualBox

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

Last change on this file since 74402 was 74402, checked in by vboxsync, 7 years ago

IPRT/rest: Implemented array and string-map support for deserializating polymorphic objects. bugref:9167

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 14.7 KB
Line 
1/* $Id: RTCRestArrayBase.cpp 74402 2018-09-21 09:25:55Z 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.215389.xyz. 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 /* Setup sub-cursor. */
159 RTCRestJsonCursor SubCursor(a_rCursor);
160 int rc = RTJsonIteratorQueryValue(hIterator, &SubCursor.m_hValue, &SubCursor.m_pszName);
161 if (RT_SUCCESS(rc))
162 {
163 char szName[32];
164 RTStrPrintf(szName, sizeof(szName), "[%u]", idxName);
165 SubCursor.m_pszName = szName;
166
167 /* Call the static deserializeInstanceFromJson method of the value class. */
168 RTCRestObjectBase *pObj = NULL;
169 rc = deserializeValueInstanceFromJson(SubCursor, &pObj);
170 if (RT_SUCCESS(rc))
171 Assert(pObj);
172 else if (RT_SUCCESS(rcRet))
173 rcRet = rc;
174 if (pObj)
175 {
176 rc = insertWorker(~(size_t)0, pObj, false /*a_fReplace*/);
177 if (RT_SUCCESS(rc))
178 { /* likely */ }
179 else
180 {
181 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "Array insert failed (index %zu): %Rrc",
182 idxName, rc);
183 delete pObj;
184 }
185 }
186 }
187 else
188 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorQueryValue failed: %Rrc", rc);
189
190 /*
191 * Advance.
192 */
193 rc = RTJsonIteratorNext(hIterator);
194 if (RT_SUCCESS(rc))
195 { /* likely */ }
196 else if (rc == VERR_JSON_ITERATOR_END)
197 break;
198 else
199 {
200 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorNext failed: %Rrc", rc);
201 break;
202 }
203 }
204
205 RTJsonIteratorFree(hIterator);
206 }
207 else if (rcRet == VERR_JSON_IS_EMPTY)
208 rcRet = VINF_SUCCESS;
209 else if ( rcRet == VERR_JSON_VALUE_INVALID_TYPE
210 && RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
211 {
212 m_fNullIndicator = true;
213 rcRet = VINF_SUCCESS;
214 }
215 else
216 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rcRet,
217 "RTJsonIteratorBeginrray failed: %Rrc (type %s)",
218 rcRet, RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
219 return rcRet;
220
221}
222
223
224int RTCRestArrayBase::toString(RTCString *a_pDst, uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/) const
225{
226 int rc;
227 if (!m_fNullIndicator)
228 {
229 if (m_cElements)
230 {
231 char const chSep = g_szSep[a_fFlags & kCollectionFormat_Mask];
232
233 rc = m_papElements[0]->toString(a_pDst, a_fFlags);
234 for (size_t i = 1; RT_SUCCESS(rc) && i < m_cElements; i++)
235 {
236 rc = a_pDst->appendNoThrow(chSep);
237 if (RT_SUCCESS(rc))
238 rc = m_papElements[i]->toString(a_pDst, a_fFlags | kToString_Append);
239 }
240 }
241 else
242 {
243 if (!(a_fFlags & kToString_Append))
244 a_pDst->setNull();
245 rc = VINF_SUCCESS;
246 }
247 }
248 else if (a_fFlags & kToString_Append)
249 rc = a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
250 else
251 rc = a_pDst->appendNoThrow(RT_STR_TUPLE("null"));
252
253 return rc;
254}
255
256
257int RTCRestArrayBase::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo /*= NULL*/,
258 uint32_t a_fFlags /*= kCollectionFormat_Unspecified*/)
259{
260 /*
261 * Clear the array. If the string is empty, we have an empty array and is done.
262 */
263 if (!(a_fFlags & kToString_Append))
264 clear();
265 if (a_rValue.isEmpty())
266 return VINF_SUCCESS;
267
268 /*
269 * Look for a separator so we don't mistake a initial null element for a null array.
270 */
271 char const chSep = g_szSep[a_fFlags & kCollectionFormat_Mask];
272 size_t offSep = a_rValue.find(chSep);
273 if ( offSep != RTCString::npos
274 || !a_rValue.startsWithWord("null", RTCString::CaseInsensitive))
275 {
276 RTCString strTmp;
277 size_t offStart = 0;
278 int rcRet = VINF_SUCCESS;
279 for (;;)
280 {
281 /* Copy the element value into its own string buffer. */
282 int rc = strTmp.assignNoThrow(a_rValue, offStart, (offSep == RTCString::npos ? a_rValue.length() : offSep) - offStart);
283 AssertRCReturn(rc, rc);
284
285 /* Create a new element, insert it and pass it the value string. */
286 RTCRestObjectBase *pObj = createValue();
287 AssertPtrReturn(pObj, VERR_NO_MEMORY);
288
289 rc = insertWorker(~(size_t)0, pObj, false);
290 AssertRCReturnStmt(rc, delete pObj, rc);
291
292 char szName[128];
293 RTStrPrintf(szName, sizeof(szName), "%.*s[%zu]", 116, a_pszName ? a_pszName : "", size());
294 rc = pObj->fromString(strTmp, a_pszName, a_pErrInfo, 0);
295 if (RT_SUCCESS(rc))
296 { /* likely */ }
297 else if (RT_SUCCESS(rcRet))
298 rcRet = rc;
299
300 /*
301 * Done? Otherwise advance.
302 */
303 if (offSep == RTCString::npos)
304 break;
305 offStart = offSep + 1;
306 offSep = a_rValue.find(chSep, offStart);
307 }
308 return rcRet;
309 }
310
311 /*
312 * Consider this a null array even if it could also be an array with a single
313 * null element. This is just an artifact of an imperfect serialization format.
314 */
315 setNull();
316 return VINF_SUCCESS;
317}
318
319
320RTCRestObjectBase::kTypeClass RTCRestArrayBase::typeClass(void) const
321{
322 return kTypeClass_Array;
323}
324
325
326const char *RTCRestArrayBase::typeName(void) const
327{
328 return "RTCRestArray<ElementType>";
329}
330
331
332
333/*********************************************************************************************************************************
334* Array methods *
335*********************************************************************************************************************************/
336
337void RTCRestArrayBase::clear()
338{
339 size_t i = m_cElements;
340 while (i-- > 0)
341 {
342 delete m_papElements[i];
343 m_papElements[i] = NULL;
344 }
345 m_cElements = 0;
346 m_fNullIndicator = false;
347}
348
349
350bool RTCRestArrayBase::removeAt(size_t a_idx)
351{
352 if (a_idx == ~(size_t)0)
353 a_idx = m_cElements - 1;
354 if (a_idx < m_cElements)
355 {
356 delete m_papElements[a_idx];
357 m_papElements[a_idx] = NULL;
358
359 m_cElements--;
360 if (a_idx < m_cElements)
361 memmove(&m_papElements[a_idx], &m_papElements[a_idx + 1], (m_cElements - a_idx) * sizeof(m_papElements[0]));
362 }
363 return false;
364}
365
366
367int RTCRestArrayBase::ensureCapacity(size_t a_cEnsureCapacity)
368{
369 if (m_cCapacity < a_cEnsureCapacity)
370 {
371 if (a_cEnsureCapacity < 512)
372 a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 16);
373 else if (a_cEnsureCapacity < 16384)
374 a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 128);
375 else
376 a_cEnsureCapacity = RT_ALIGN_Z(a_cEnsureCapacity, 512);
377
378 void *pvNew = RTMemRealloc(m_papElements, sizeof(m_papElements[0]) * a_cEnsureCapacity);
379 if (pvNew)
380 {
381 m_papElements = (RTCRestObjectBase **)pvNew;
382 memset(&m_papElements[m_cCapacity], 0, (a_cEnsureCapacity - m_cCapacity) * sizeof(sizeof(m_papElements[0])));
383 m_cCapacity = a_cEnsureCapacity;
384 }
385 else
386 return VERR_NO_MEMORY;
387 }
388 return VINF_SUCCESS;
389}
390
391
392int RTCRestArrayBase::copyArrayWorker(RTCRestArrayBase const &a_rThat, bool a_fThrow)
393{
394 int rc;
395 clear();
396 if (a_rThat.m_cElements == 0)
397 {
398 m_fNullIndicator = a_rThat.m_fNullIndicator;
399 rc = VINF_SUCCESS;
400 }
401 else
402 {
403 Assert(!a_rThat.m_fNullIndicator);
404 rc = ensureCapacity(a_rThat.m_cElements);
405 if (RT_SUCCESS(rc))
406 {
407 for (size_t i = 0; i < a_rThat.m_cElements; i++)
408 {
409 AssertPtr(a_rThat.m_papElements[i]);
410 rc = insertCopyWorker(i, *a_rThat.m_papElements[i], false);
411 if (RT_SUCCESS(rc))
412 { /* likely */ }
413 else if (a_fThrow)
414 throw std::bad_alloc();
415 else
416 return rc;
417 }
418 }
419 }
420 return rc;
421}
422
423
424int RTCRestArrayBase::insertWorker(size_t a_idx, RTCRestObjectBase *a_pValue, bool a_fReplace)
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)
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.

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