VirtualBox

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

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

IPRT/rest: Reimplement RTCRestArrayBase::fromString and adjusted response header handling of arrays to deal with multiple headers for the same field (just in case). bugref:9167

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