VirtualBox

source: vbox/trunk/src/VBox/Runtime/common/rest/RTCRestStringMapBase.cpp@ 87203

Last change on this file since 87203 was 86778, checked in by vboxsync, 4 years ago

IPRT/rest: bugref:9167 - don't try to deserialize null as if it were
an object. Check for it explicitly and punt. This avoids an
assertion in a debug build. While here add a few more explicit
isNull() checks.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 13.8 KB
Line 
1/* $Id: RTCRestStringMapBase.cpp 86778 2020-10-31 01:42:13Z vboxsync $ */
2/** @file
3 * IPRT - C++ REST, RTCRestStringMapBase implementation.
4 */
5
6/*
7 * Copyright (C) 2018-2020 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/reststringmap.h>
33
34#include <iprt/err.h>
35#include <iprt/string.h>
36#include <iprt/cpp/restoutput.h>
37
38
39/**
40 * Default destructor.
41 */
42RTCRestStringMapBase::RTCRestStringMapBase() RT_NOEXCEPT
43 : RTCRestObjectBase()
44 , m_Map(NULL)
45 , m_cEntries(0)
46{
47 RTListInit(&m_ListHead);
48}
49
50
51#if 0 /* trigger link error for now. */
52/** Copy constructor. */
53RTCRestStringMapBase::RTCRestStringMapBase(RTCRestStringMapBase const &a_rThat);
54#endif
55
56
57/**
58 * Destructor.
59 */
60RTCRestStringMapBase::~RTCRestStringMapBase()
61{
62 clear();
63}
64
65
66
67#if 0 /* trigger link error for now. */
68/** Copy assignment operator. */
69RTCRestStringMapBase &RTCRestStringMapBase::operator=(RTCRestStringMapBase const &a_rThat);
70#endif
71
72
73/*********************************************************************************************************************************
74* Overridden base object methods *
75*********************************************************************************************************************************/
76
77RTCRestObjectBase *RTCRestStringMapBase::baseClone() const RT_NOEXCEPT
78{
79 RTCRestStringMapBase *pClone = createClone();
80 if (pClone)
81 {
82 int rc = pClone->copyMapWorkerNoThrow(*this);
83 if (RT_SUCCESS(rc))
84 return pClone;
85 delete pClone;
86 }
87 return NULL;
88}
89
90
91int RTCRestStringMapBase::resetToDefault() RT_NOEXCEPT
92{
93 /* Default is an empty map. */
94 clear();
95 m_fNullIndicator = false;
96 return VINF_SUCCESS;
97}
98
99
100RTCRestOutputBase &RTCRestStringMapBase::serializeAsJson(RTCRestOutputBase &a_rDst) const RT_NOEXCEPT
101{
102 if (!m_fNullIndicator)
103 {
104 uint32_t const uOldState = a_rDst.beginObject();
105 MapEntry const *pCur;
106 RTListForEachCpp(&m_ListHead, pCur, MapEntry, ListEntry)
107 {
108 a_rDst.valueSeparatorAndName(pCur->strKey.c_str(), pCur->strKey.length());
109 pCur->pValue->serializeAsJson(a_rDst);
110 }
111 a_rDst.endObject(uOldState);
112 }
113 else
114 a_rDst.nullValue();
115 return a_rDst;
116}
117
118
119int RTCRestStringMapBase::deserializeFromJson(RTCRestJsonCursor const &a_rCursor) RT_NOEXCEPT
120{
121 if (RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
122 {
123 setNull();
124 return VINF_SUCCESS;
125 }
126
127 /*
128 * Make sure the object starts out with an empty map.
129 */
130 if (m_cEntries > 0)
131 clear();
132 m_fNullIndicator = false;
133
134 /*
135 * Iterate the object values.
136 */
137 RTJSONIT hIterator;
138 int rcRet = RTJsonIteratorBeginObject(a_rCursor.m_hValue, &hIterator);
139 if (RT_SUCCESS(rcRet))
140 {
141 for (;;)
142 {
143 /* Set up the sub-cursor. */
144 RTCRestJsonCursor SubCursor(a_rCursor);
145 int rc = RTJsonIteratorQueryValue(hIterator, &SubCursor.m_hValue, &SubCursor.m_pszName);
146 if (RT_SUCCESS(rc))
147 {
148 /* Call the static deserializeInstanceFromJson method of the value class. */
149 RTCRestObjectBase *pObj = NULL;
150 rc = deserializeValueInstanceFromJson(SubCursor, &pObj);
151 if (RT_SUCCESS(rc))
152 Assert(pObj);
153 else if (RT_SUCCESS(rcRet))
154 rcRet = rc;
155 if (pObj)
156 {
157 /* Insert the value. */
158 rc = putWorker(SubCursor.m_pszName, pObj, true /*a_fReplace*/);
159 if (rc == VINF_SUCCESS)
160 { /* likely */ }
161 else if (RT_SUCCESS(rc))
162 {
163 a_rCursor.m_pPrimary->addError(a_rCursor, rc, "warning %Rrc inserting '%s' into map",
164 rc, SubCursor.m_pszName);
165 if (rcRet == VINF_SUCCESS)
166 rcRet = rc;
167 }
168 else
169 {
170 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "Failed to insert '%s' into map: %Rrc",
171 SubCursor.m_pszName, rc);
172 delete pObj;
173 }
174 }
175 }
176 else
177 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorQueryValue failed: %Rrc", rc);
178
179 /*
180 * Advance.
181 */
182 rc = RTJsonIteratorNext(hIterator);
183 if (RT_SUCCESS(rc))
184 { /* likely */ }
185 else if (rc == VERR_JSON_ITERATOR_END)
186 break;
187 else
188 {
189 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rc, "RTJsonIteratorNext failed: %Rrc", rc);
190 break;
191 }
192 }
193
194 RTJsonIteratorFree(hIterator);
195 }
196 else if (rcRet == VERR_JSON_IS_EMPTY)
197 rcRet = VINF_SUCCESS;
198 else if ( rcRet == VERR_JSON_VALUE_INVALID_TYPE
199 && RTJsonValueGetType(a_rCursor.m_hValue) == RTJSONVALTYPE_NULL)
200 {
201 m_fNullIndicator = true;
202 rcRet = VINF_SUCCESS;
203 }
204 else
205 rcRet = a_rCursor.m_pPrimary->addError(a_rCursor, rcRet, "RTJsonIteratorBegin failed: %Rrc (type %s)",
206 rcRet, RTJsonValueTypeName(RTJsonValueGetType(a_rCursor.m_hValue)));
207 return rcRet;
208}
209
210// later?
211// virtual int RTCRestStringMapBase::toString(RTCString *a_pDst, uint32_t a_fFlags = kCollectionFormat_Unspecified) const ;
212// virtual int RTCRestStringMapBase::fromString(RTCString const &a_rValue, const char *a_pszName, PRTERRINFO a_pErrInfo = NULL,
213// uint32_t a_fFlags = kCollectionFormat_Unspecified) ;
214//
215
216
217RTCRestObjectBase::kTypeClass RTCRestStringMapBase::typeClass(void) const RT_NOEXCEPT
218{
219 return kTypeClass_StringMap;
220}
221
222
223const char *RTCRestStringMapBase::typeName(void) const RT_NOEXCEPT
224{
225 return "RTCRestStringMap<ValueType>";
226}
227
228
229/*********************************************************************************************************************************
230* Generic map methods *
231*********************************************************************************************************************************/
232
233/**
234 * @callback_method_impl{FNRTSTRSPACECALLBACK}
235 */
236/*static*/ DECLCALLBACK(int) RTCRestStringMapBase::stringSpaceDestructorCallback(PRTSTRSPACECORE pStr, void *pvUser) RT_NOEXCEPT
237{
238 MapEntry *pNode = (MapEntry *)pStr;
239 if (pNode->pValue)
240 {
241 delete pNode->pValue;
242 pNode->pValue = NULL;
243 }
244 pNode->strKey.setNull();
245 delete pNode;
246
247 RT_NOREF(pvUser);
248 return VINF_SUCCESS;
249}
250
251
252void RTCRestStringMapBase::clear() RT_NOEXCEPT
253{
254 RTStrSpaceDestroy(&m_Map, stringSpaceDestructorCallback, NULL);
255 RTListInit(&m_ListHead);
256 m_cEntries = 0;
257 m_fNullIndicator = false;
258}
259
260
261size_t RTCRestStringMapBase::size() const RT_NOEXCEPT
262{
263 return m_cEntries;
264}
265
266
267bool RTCRestStringMapBase::containsKey(const char *a_pszKey) const RT_NOEXCEPT
268{
269 if (isNull())
270 return false;
271
272 return RTStrSpaceGet((PRTSTRSPACE)&m_Map, a_pszKey) != NULL;
273}
274
275
276bool RTCRestStringMapBase::containsKey(RTCString const &a_rStrKey) const RT_NOEXCEPT
277{
278 return containsKey(a_rStrKey.c_str());
279}
280
281
282bool RTCRestStringMapBase::remove(const char *a_pszKey) RT_NOEXCEPT
283{
284 if (isNull())
285 return false;
286
287 MapEntry *pRemoved = (MapEntry *)RTStrSpaceRemove(&m_Map, a_pszKey);
288 if (pRemoved)
289 {
290 m_cEntries--;
291 RTListNodeRemove(&pRemoved->ListEntry);
292 stringSpaceDestructorCallback(&pRemoved->Core, NULL);
293 return true;
294 }
295 return false;
296}
297
298
299bool RTCRestStringMapBase::remove(RTCString const &a_rStrKey) RT_NOEXCEPT
300{
301 return remove(a_rStrKey.c_str());
302}
303
304
305int RTCRestStringMapBase::putNewValue(RTCRestObjectBase **a_ppValue, const char *a_pszKey, size_t a_cchKey /*= RTSTR_MAX*/,
306 bool a_fReplace /*= false*/) RT_NOEXCEPT
307{
308 RTCRestObjectBase *pValue = createValue();
309 if (pValue)
310 {
311 int rc = putWorker(a_pszKey, pValue, a_fReplace, a_cchKey);
312 if (RT_SUCCESS(rc))
313 *a_ppValue = pValue;
314 else
315 {
316 delete pValue;
317 *a_ppValue = NULL;
318 }
319 return rc;
320 }
321 *a_ppValue = NULL;
322 return VERR_NO_MEMORY;
323}
324
325
326int RTCRestStringMapBase::putNewValue(RTCRestObjectBase **a_ppValue, RTCString const &a_rStrKey, bool a_fReplace /*= false*/) RT_NOEXCEPT
327{
328 return putNewValue(a_ppValue, a_rStrKey.c_str(), a_rStrKey.length(), a_fReplace);
329}
330
331
332/*********************************************************************************************************************************
333* Protected methods *
334*********************************************************************************************************************************/
335
336int RTCRestStringMapBase::copyMapWorkerNoThrow(RTCRestStringMapBase const &a_rThat) RT_NOEXCEPT
337{
338 Assert(this != &a_rThat);
339 clear();
340 m_fNullIndicator = a_rThat.m_fNullIndicator;
341
342 if (!a_rThat.m_fNullIndicator)
343 {
344 MapEntry const *pCur;
345 RTListForEachCpp(&a_rThat.m_ListHead, pCur, MapEntry, ListEntry)
346 {
347 int rc = putCopyWorker(pCur->strKey.c_str(), *pCur->pValue, true /*a_fReplace*/);
348 if (RT_SUCCESS(rc))
349 { /* likely */ }
350 else
351 return rc;
352 }
353 }
354
355 return VINF_SUCCESS;
356}
357
358
359void RTCRestStringMapBase::copyMapWorkerMayThrow(RTCRestStringMapBase const &a_rThat)
360{
361 int rc = copyMapWorkerNoThrow(a_rThat);
362 if (RT_SUCCESS(rc))
363 return;
364 throw std::bad_alloc();
365}
366
367
368int RTCRestStringMapBase::putWorker(const char *a_pszKey, RTCRestObjectBase *a_pValue, bool a_fReplace,
369 size_t a_cchKey /*= RTSTR_MAX*/) RT_NOEXCEPT
370{
371 int rc;
372 MapEntry *pEntry = new (std::nothrow) MapEntry;
373 if (pEntry)
374 {
375 rc = pEntry->strKey.assignNoThrow(a_pszKey, a_cchKey);
376 if (RT_SUCCESS(rc))
377 {
378 pEntry->Core.pszString = pEntry->strKey.c_str();
379 pEntry->Core.cchString = pEntry->strKey.length();
380 pEntry->pValue = a_pValue;
381 if (RTStrSpaceInsert(&m_Map, &pEntry->Core))
382 {
383 RTListAppend(&m_ListHead, &pEntry->ListEntry);
384 m_cEntries++;
385 m_fNullIndicator = false;
386 return VINF_SUCCESS;
387 }
388
389 Assert(!m_fNullIndicator);
390 if (!a_fReplace)
391 rc = VERR_ALREADY_EXISTS;
392 else
393 {
394 /* Just replace the pValue in the existing entry. */
395 MapEntry *pCollision = (MapEntry *)RTStrSpaceGet(&m_Map, a_pszKey);
396 if (pCollision)
397 {
398 if (pCollision->pValue)
399 delete pCollision->pValue;
400 pCollision->pValue = a_pValue;
401 pEntry->pValue = NULL; /* paranoia */
402 rc = VWRN_ALREADY_EXISTS;
403 }
404 else
405 rc = VERR_INTERNAL_ERROR;
406 }
407 }
408 delete pEntry;
409 }
410 else
411 rc = VERR_NO_MEMORY;
412 return rc;
413}
414
415
416int RTCRestStringMapBase::putCopyWorker(const char *a_pszKey, RTCRestObjectBase const &a_rValue, bool a_fReplace,
417 size_t a_cchKey /*= RTSTR_MAX*/) RT_NOEXCEPT
418{
419 int rc;
420 RTCRestObjectBase *pValueCopy = a_rValue.baseClone();
421 if (pValueCopy)
422 {
423 rc = putWorker(a_pszKey, pValueCopy, a_fReplace, a_cchKey);
424 if (RT_SUCCESS(rc))
425 { /* likely */ }
426 else
427 delete pValueCopy;
428 }
429 else
430 rc = VERR_NO_MEMORY;
431 return rc;
432}
433
434
435RTCRestObjectBase *RTCRestStringMapBase::getWorker(const char *a_pszKey) RT_NOEXCEPT
436{
437 if (isNull())
438 return NULL;
439
440 MapEntry *pHit = (MapEntry *)RTStrSpaceGet(&m_Map, a_pszKey);
441 if (pHit)
442 return pHit->pValue;
443 return NULL;
444}
445
446
447RTCRestObjectBase const *RTCRestStringMapBase::getWorker(const char *a_pszKey) const RT_NOEXCEPT
448{
449 if (isNull())
450 return NULL;
451
452 MapEntry const *pHit = (MapEntry const *)RTStrSpaceGet((PRTSTRSPACE)&m_Map, a_pszKey);
453 if (pHit)
454 return pHit->pValue;
455 return NULL;
456}
457
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