Teamcenter C++ API Reference  2312
PackedMemoryBuilder.hxx
Go to the documentation of this file.
1 // Copyright 2022 Siemens Digital Industries Software
2 // ==================================================
3 // Copyright 2016.
4 // Siemens Product Lifecycle Management Software Inc.
5 // All Rights Reserved.
6 // ==================================================
7 // Copyright 2022 Siemens Digital Industries Software
8 
19 #ifndef PACKED_MEMORY_BUILDER_HXX
20 #define PACKED_MEMORY_BUILDER_HXX
21 
22 #include <map>
23 #include <set>
24 #include <string.h>
25 #include <vector>
26 #include <base_utils/base_utils_errors.h>
27 #include <base_utils/IFail.hxx>
28 #include <base_utils/Mem.h>
29 #include <base_utils/SharedPtr.hxx>
30 
31 namespace Teamcenter
32 {
33  namespace PackedMemory
34  {
35 
41 inline size_t getAlignedSize(
42  size_t datasize
43  )
44 {
45  // This should give us the machine alignment on a pointer.
46  const struct { char c; char *ptr; } *pAlignment= 0;
47  const size_t alignment= size_t(&pAlignment->ptr);
48 
49  return (datasize + alignment - 1) / alignment * alignment;
50 }
51 
54 {
58  void * alloc(
59  size_t size
60  ) const
61  {
62  // Forward to MEM_alloc to do the allocation.
63  return MEM_alloc((int)size);
64  }
65 
69  void *alloc_packed(
70  size_t dataToPackCount,
71  MEM_data_to_pack_p_t pDataToPack,
72  logical allowOverride
73  ) const
74  {
75  // Forward to MEM_alloc_packed to do the allocation.
76  return MEM_alloc_packed(dataToPackCount, pDataToPack, allowOverride);
77  }
78 
82  void free(
83  void *pAddress
84  ) const
85  {
86  // Forward to MEM_alloc to do the allocation.
87  MEM_free(pAddress);
88  }
89 };
90 
102 {
106 };
107 
108 
175 
269 template <class T, typename Alloc = DefaultAllocator>
270 class Builder
271 {
272 public:
281  explicit Builder(size_t elements= 1);
282 
293  explicit Builder(const char *pSrcString, size_t maxLength= ~0);
294 
298  T *get() { return m_pObjectData; }
299 
303  const T *get() const { return m_pObjectData; }
304 
316  void setLookupStringData(bool lookupStringData) { m_lookupStringData= lookupStringData; }
317 
320  bool getLookupStringData() const { return m_lookupStringData; }
321 
332  template <typename K>
333  inline K addString(K &pDestPtr, const char *pSrcString);
334 
346  template <typename K>
347  K addData(K &pDestPtr, size_t elements= 1, const void *pSourceData= NULL);
348 
360  template <typename K, typename J>
361  K addReference(K &pDestPtr, J &pReferenceData);
362 
373  void createBuffer(T **pDestination, CreateMode createMode = NoMarking) const;
374 
378  Alloc& allocator() { return m_allocator; }
379 
383  const Alloc& allocator() const { return m_allocator; }
384 
385 private:
387  Builder(const Builder &);
389  Builder & operator = (const Builder &);
390 
403  struct BackRefData
404  {
405  BackRefData() : pData(NULL), datasize(0) {}
406  BackRefData(void *_pData, size_t _datasize) : pData(_pData), datasize(_datasize) {}
407 
408  void *pData;
409  size_t datasize;
410  };
411 
420  struct StringData
421  {
422  StringData() : m_pString(NULL), m_datasize(0) {}
423  StringData(const char *pString, size_t datasize) : m_pString(pString), m_datasize(datasize) {}
424 
425  bool operator < (const StringData &rhs) const
426  {
427  return m_datasize < rhs.m_datasize ||
428  (m_datasize == rhs.m_datasize && m_pString && rhs.m_pString && memcmp(m_pString, rhs.m_pString, m_datasize) < 0);
429  }
430 
431  const char *m_pString;
432  size_t m_datasize;
433  };
434 
435 
436 
437  typedef std::vector<char> Buffer;
438  typedef std::vector<void *> ReferencedObjectsList;
439  typedef std::map<void *, Buffer> ObjectDataMap;
440  typedef std::map<void *, ReferencedObjectsList> ReferencedObjectsMap;
441  typedef std::map<void *, void *> ObjectPointerMap;
442  typedef std::map<const void *, BackRefData> ObjectBackRefDataMap;
443  typedef std::set<StringData> StringDataSet;
444 
445 
446 
452 
454  Alloc m_allocator;
456 
467  bool getMappedObject(const void *pBaseAddress, void *&pMappedObject, bool bBaseOnly= false) const;
468 
480  void createOutputBuffer(ObjectPointerMap &destinationMap, T **pDestination) const;
481 
494  void createOutputBufferUsingSMPack(ObjectPointerMap &destinationMap, T **pDestination, logical allowOverride) const;
495 };
496 
497 template <class T, typename Alloc>
498 Builder<T, Alloc>::Builder(size_t elements):
499  m_pObjectData(NULL), m_lookupStringData(false)
500 {
501  // Figure out the size we need.
502  size_t datasize= getAlignedSize(sizeof(T) * elements);
503 
504  // Create the buffer for the object(s) T we are storing.
505  // We use NULL to indicate the base object. This should now always
506  // be ordered first in the map wich is important.
507  Buffer &buffer= m_objectDataMap[NULL];
508  buffer.resize(datasize);
509 
510  // Also populate the back reference.
511  m_objectBackRefDataMap[&buffer[0]]= BackRefData(NULL, datasize);
512 
513  // Assign the data to the buffer for quick access.
514  m_pObjectData= datasize ? reinterpret_cast<T *>(&buffer[0]) : NULL;
515 }
516 
517 template <class T, typename Alloc>
518 Builder<T, Alloc>::Builder(const char *pSrcString, size_t maxLength):
519  m_pObjectData(NULL), m_lookupStringData(false)
520 {
521  // What the size we need for this string.
522  const size_t stringSize= pSrcString ? strlen(pSrcString) + 1 : 0;
523  const size_t datasize= stringSize < maxLength ? stringSize : maxLength;
524 
525  // Create the buffer for the object(s) T we are storing.
526  // We use NULL to indicate the base object. This should now always
527  // be ordered first in the map wich is important.
528  Buffer &buffer= m_objectDataMap[NULL];
529  buffer.resize(datasize);
530 
531  // Also populate the back reference.
532  m_objectBackRefDataMap[&buffer[0]]= BackRefData(NULL, datasize);
533 
534  // Assign the data to the buffer for quick access.
535  m_pObjectData= datasize ? reinterpret_cast<T *>(&buffer[0]) : NULL;
536 
537  // If we have the data to copy, do it now.
538  if (pSrcString && datasize)
539  {
540  strncpy(&buffer[0], pSrcString, datasize);
541  buffer[datasize - 1] = '\0';
542  }
543 }
544 
545 template <class T, typename Alloc> template <typename K>
546 K Builder<T, Alloc>::addString(K &pDestPtr, const char *pSrcString)
547 {
548  // What the size we need for this string.
549  const size_t datasize= pSrcString ? strlen(pSrcString) + 1 : 0;
550 
551  // If we have a string,
552  if (datasize && m_lookupStringData)
553  {
554  // see if we can find the string in our string reference set.
555  typename StringDataSet::iterator searchIter = m_addedStringData.find(StringData(pSrcString, datasize));
556  // If we already have seen this string,
557  if (searchIter != m_addedStringData.end())
558  {
559  // start by casting the found string to the proper output type.
560  K pReferenceData = reinterpret_cast<K>(const_cast<char *>(searchIter->m_pString));
561  // Now we can add the reference to the found string to the destination and return value.
562  return addReference(pDestPtr, pReferenceData);
563  }
564  else
565  {
566  // We have not seen this before so forward having the pSourceData as the string data.
567  K pAddedData = addData(pDestPtr, datasize, pSrcString);
568  // Make sure we add the string into the StringReferenceData set (we do handle nulls).
569  m_addedStringData.insert(StringData(pAddedData, datasize));
570 
571  // Return the string we added.
572  return pAddedData;
573  }
574  }
575 
576  // This is either a NULL pointer or we are not doing lookups in the string collection set.
577  // Just forward to addData having the pSourceData as the string data.
578  return addData(pDestPtr, datasize, pSrcString);
579 }
580 
581 template <class T, typename Alloc> template <typename K>
582 K Builder<T, Alloc>::addData(K &pDestPtr, size_t elements, const void *pSourceData)
583 {
584  // What the size we need for these referenced data.
585  const size_t datasize= getAlignedSize(sizeof(*pDestPtr) * elements);
586 
587  // The address of the parameter K &pDestPtr.
588  void *pParameterAddess= (void *)&pDestPtr;
589 
590  // Make sure that we have a pointer address that we have seen before. We can't add data
591  // unless we reference some of the external data buffers.
592  void *pBaseObject= NULL;
593  if (!getMappedObject(pParameterAddess, pBaseObject))
594  {
595  throw IFail( BASE_UTILS_invalid_reference );
596  }
597 
598  // See if we have seen this before, this is most likely a coding error.
599  ObjectDataMap::const_iterator searchIter= m_objectDataMap.find(pParameterAddess);
600  if (searchIter != m_objectDataMap.end())
601  {
602  throw IFail( BASE_UTILS_already_initialized, "m_objectDataMap" );
603  }
604 
605  // Add new allocation we did for this base object.
606  m_referencedObjects[pBaseObject].push_back(pParameterAddess);
607 
608  // Create the buffer for the object(s) T we are storing.
609  Buffer &buffer= m_objectDataMap[pParameterAddess];
610  buffer.resize(datasize);
611 
612  // Also populate the back reference.
613  m_objectBackRefDataMap[&buffer[0]]= BackRefData(pParameterAddess, datasize);
614 
615  // If we have source data,
616  if (pSourceData && datasize)
617  {
618  // copy the data to the destination.
619  memcpy(&buffer[0], pSourceData, datasize);
620  }
621 
622  // Set the pointer to the buffer we have allocated.
623  pDestPtr= datasize ? reinterpret_cast<K>(&buffer[0]) : NULL;
624 
625  // Give out the pointer to the caller.
626  return pDestPtr;
627 }
628 
629 template <class T, typename Alloc> template <typename K, typename J>
630 K Builder<T, Alloc>::addReference(K &pDestPtr, J &pReferenceData)
631 {
632  // The address of the parameter K &pDestPtr.
633  void *pParameterAddess= (void *)&pDestPtr;
634 
635  // Make sure that we have a pointer address that we have seen before. We can't add data
636  // unless we reference some of the external data buffers.
637  void *pBaseObject= NULL;
638  if (!getMappedObject(pParameterAddess, pBaseObject))
639  {
640  throw IFail( BASE_UTILS_invalid_reference );
641  }
642 
643  // See if we have seen this before, this is most likely a coding error.
644  ObjectDataMap::const_iterator searchIter= m_objectDataMap.find(pParameterAddess);
645  if (searchIter != m_objectDataMap.end())
646  {
647  throw IFail( BASE_UTILS_already_initialized, "m_objectDataMap" );
648  }
649 
650  // Make sure that we have a pointer address that we have seen before. We can't add data
651  // unless we reference some of the external data buffers that we have created.
652  void *pRefBaseObject= NULL;
653  if (!getMappedObject(pReferenceData, pRefBaseObject, true))
654  {
655  throw IFail( BASE_UTILS_invalid_reference );
656  }
657 
658  // Add new allocation we did for this base object.
659  m_referencedObjects[pBaseObject].push_back(pParameterAddess);
660 
661  // Add new back reference to the existing buffer.
662  m_backReferencedObjects[pParameterAddess]= pRefBaseObject;
663 
664  // Create the buffer for the object(s) T we are storing with zero length.
665  // This will indicate that we should look in the back reference for the data.
666  Buffer &buffer= m_objectDataMap[pParameterAddess];
667  buffer.resize(0);
668 
669  // Also populate the back reference.
670  m_objectBackRefDataMap[&buffer[0]] = BackRefData(pParameterAddess, 0);
671 
672  // Set the desination pointer to the reference data.
673  pDestPtr= pReferenceData;
674 
675  // Give out the pointer to the caller.
676  return pDestPtr;
677 }
678 
679 template <class T, typename Alloc>
680 void Builder<T, Alloc>::createBuffer(T **pDestination, CreateMode createMode) const
681 {
682  // Create the output data and give the object mapping back to us.
683  ObjectPointerMap destinationMap;
684 
685  // See how we are creating the buffer.
686  if (createMode == NoMarking)
687  {
688  // This will create a tight packing.
689  createOutputBuffer(destinationMap, pDestination);
690  }
691  else
692  {
693  // Create a packed buffer with marked allocation (unless override is enabled).
694  createOutputBufferUsingSMPack(destinationMap, pDestination, createMode == AllowOverride);
695  }
696 
697  // Loop over all the extra reference object we have added.
698  ReferencedObjectsMap::const_iterator referencedIter= m_referencedObjects.begin();
699  for ( ; referencedIter != m_referencedObjects.end(); ++referencedIter)
700  {
701  // Find the mapped Object Data for this reference.
702  ObjectDataMap::const_iterator objectDataIter= m_objectDataMap.find(referencedIter->first);
703  if (objectDataIter == m_objectDataMap.end())
704  {
705  throw IFail( BASE_UTILS_expected_data_missing, "m_objectDataMap" );
706  }
707 
708  // We also need to find this object in the output buffer.
709  ObjectPointerMap::iterator destinationObject= destinationMap.find(referencedIter->first);
710  if (destinationObject == destinationMap.end())
711  {
712  throw IFail( BASE_UTILS_expected_data_missing, "destinationMap" );
713  }
714 
715  // Loop over all the objects we reference to this object and update the desination.
716  ReferencedObjectsList::const_iterator childIter= referencedIter->second.begin();
717  for ( ; childIter != referencedIter->second.end(); ++childIter)
718  {
719  // Find this object in the destination map.
720  ObjectPointerMap::iterator referencedDestinationObject= destinationMap.find(*childIter);
721  if (referencedDestinationObject == destinationMap.end())
722  {
723  throw IFail( BASE_UTILS_expected_data_missing, "destinationMap" );
724  }
725 
726  // Calculate the offset where this object is located in the referenced data structure.
727  size_t offset= size_t(*childIter) - size_t(&objectDataIter->second[0]);
728 
729  // Now we can re-point that pointer to the new destination location.
730  *reinterpret_cast<void **>(size_t(destinationObject->second) + offset) = referencedDestinationObject->second;
731  }
732  }
733 }
734 
735 
736 template <class T, typename Alloc>
737 bool Builder<T, Alloc>::getMappedObject(const void *pBaseAddress, void *&pMappedObject, bool bBaseOnly) const
738 {
739  // If we have a base address and we have something mapped,
740  if (pBaseAddress != NULL && !m_objectBackRefDataMap.empty())
741  {
742  // First find the address. Try to find an address that is the next bigger pointer.
743  typename ObjectBackRefDataMap::const_iterator searchIter = m_objectBackRefDataMap.upper_bound(pBaseAddress);
744  // If we are not at the beginning, move to the prior element. This should be the object for the pBaseAddress.
745  if (searchIter != m_objectBackRefDataMap.begin()) { --searchIter; }
746 
747  // Figure out the end pointer address for the buffer.
748  const void *pObjectEnd = reinterpret_cast<const char *>(searchIter->first) + searchIter->second.datasize;
749 
750  // If the address is within the object, the reference is good.
751  if ((bBaseOnly && pBaseAddress == searchIter->first) || (searchIter->first <= pBaseAddress && pBaseAddress < pObjectEnd))
752  {
753  // Assign the pMappedObject with the pData value we mapped and return true.
754  pMappedObject = searchIter->second.pData;
755  return true;
756  }
757  }
758 
759  // Could not find a object that encapsulate the address. Assign NULL to pMappedObject and return false.
760  pMappedObject = NULL;
761  return false;
762 }
763 
764 template <class T, typename Alloc>
765 void Builder<T, Alloc>::createOutputBuffer(ObjectPointerMap &destinationMap, T **pDestination) const
766 {
767  // Make sure we have an output pointer.
768  if (!pDestination)
769  {
770  throw IFail( BASE_UTILS_invalid_parameter );
771  }
772 
773  // Size of the buffer we need.
774  size_t totalSize= 0;
775 
776  // Count up all of the buffers we have allocated including the our object...
777  ObjectDataMap::const_iterator objectDataIter= m_objectDataMap.begin();
778  for ( ; objectDataIter != m_objectDataMap.end(); ++objectDataIter)
779  {
780  totalSize+= objectDataIter->second.size();
781  }
782 
783  // Allocate the output buffer using the specific Alloc.
784  char *pOutputBuffer= totalSize ? reinterpret_cast<char *>(allocator().alloc(totalSize)) : NULL;
785  // Make sure we have a buffer if expected.
786  if (totalSize && !pOutputBuffer)
787  {
788  throw IFail( BASE_UTILS_no_memory );
789  }
790 
791  // We assing this now to the destination so we have a better chance not to leak the memory.
792  // Just incase we throw an exception while processing the data to the buffer.
793  *pDestination= totalSize ? reinterpret_cast<T *>(pOutputBuffer) : NULL;
794 
795  // This is the end of the buffer where the "pOutputBuffer" should be
796  // after we have processed all the data, if not, we failed.
797  char *pOutputBufferEnd= pOutputBuffer + totalSize;
798 
799  // Now we can start fixing up all the pointer data.
800  objectDataIter= m_objectDataMap.begin();
801  for ( ; objectDataIter != m_objectDataMap.end(); ++objectDataIter)
802  {
803  // If we don't have any data,
804  if (objectDataIter->second.empty())
805  {
806  // just set the destination as NULL.
807  destinationMap[objectDataIter->first]= NULL;
808  }
809  else
810  {
811  // otherwise copy the data and move the output buffer pointer.
812  memcpy(pOutputBuffer, &objectDataIter->second[0], objectDataIter->second.size());
813  destinationMap[objectDataIter->first]= pOutputBuffer;
814  pOutputBuffer+= objectDataIter->second.size();
815  }
816  }
817 
818 
819  // Also add all the back reference data.
820  ObjectPointerMap::const_iterator backRefIter= m_backReferencedObjects.begin();
821  for ( ; backRefIter != m_backReferencedObjects.end(); ++backRefIter)
822  {
823  // Find the back reference data in the destination. This should be NULL.
824  ObjectPointerMap::iterator backRefDataIter= destinationMap.find(backRefIter->first);
825  if (backRefDataIter == destinationMap.end() || backRefDataIter->second != NULL)
826  {
827  throw IFail( BASE_UTILS_internal_error );
828  }
829 
830  // We should also be able to find referenced data in the destination.
831  ObjectPointerMap::const_iterator searchIter= destinationMap.find(backRefIter->second);
832  if (searchIter == destinationMap.end() || searchIter->second == NULL)
833  {
834  throw IFail( BASE_UTILS_internal_error );
835  }
836 
837  // Add another destination mapping to exiting data.
838  backRefDataIter->second= searchIter->second;
839  }
840 
841  // Cross the "T's" :)... We should have moved the pointer as expected and the "NULL"
842  // mapped object pointing to the beginning of the buffer.
843  if (pOutputBuffer != pOutputBufferEnd || destinationMap[NULL] != *pDestination)
844  {
845  throw IFail( BASE_UTILS_internal_error );
846  }
847 }
848 
849 template <class T, typename Alloc>
850 void Builder<T, Alloc>::createOutputBufferUsingSMPack(ObjectPointerMap &destinationMap, T **pDestination, logical allowOverride) const
851 {
852  // Make sure we have an output pointer.
853  if (!pDestination)
854  {
855  throw IFail(BASE_UTILS_invalid_parameter);
856  }
857 
858  // This will hold the information we are packing.
859  std::vector<MEM_data_to_pack_t> dataToPack;
860  dataToPack.reserve(m_objectDataMap.size());
861 
862  // Count up all of the buffers we have allocated including the our object...
863  ObjectDataMap::const_iterator objectDataIter = m_objectDataMap.begin();
864  for (; objectDataIter != m_objectDataMap.end(); ++objectDataIter)
865  {
866  MEM_data_to_pack_t data;
867  data.data = !objectDataIter->second.empty() ? objectDataIter->second.data() : NULL;
868  data.data_size = objectDataIter->second.size();
869 
870  dataToPack.push_back(data);
871  }
872 
873  // We assing the allocation to the destination so we have a better chance not to leak the memory.
874  // Just incase we throw an exception while processing the data to the buffer.
875  *pDestination = reinterpret_cast<T *>(allocator().alloc_packed(dataToPack.size(), &dataToPack[0], allowOverride));
876  // Make sure we have a buffer if expected.
877  if (dataToPack.front().data_size && !*pDestination)
878  {
879  throw IFail(BASE_UTILS_no_memory);
880  }
881 
882  // Now we can start fixing up all the pointer data.
883  objectDataIter = m_objectDataMap.begin();
884  for (size_t i = 0; objectDataIter != m_objectDataMap.end(); ++i, ++objectDataIter)
885  {
886  destinationMap[objectDataIter->first] = (void *)dataToPack[i].data;
887  }
888 
889  // Also add all the back reference data.
890  ObjectPointerMap::const_iterator backRefIter = m_backReferencedObjects.begin();
891  for (; backRefIter != m_backReferencedObjects.end(); ++backRefIter)
892  {
893  // Find the back reference data in the destination. This should be NULL.
894  ObjectPointerMap::iterator backRefDataIter = destinationMap.find(backRefIter->first);
895  if (backRefDataIter == destinationMap.end() || backRefDataIter->second != NULL)
896  {
897  throw IFail(BASE_UTILS_internal_error);
898  }
899 
900  // We should also be able to find referenced data in the destination.
901  ObjectPointerMap::const_iterator searchIter = destinationMap.find(backRefIter->second);
902  if (searchIter == destinationMap.end() || searchIter->second == NULL)
903  {
904  throw IFail(BASE_UTILS_internal_error);
905  }
906 
907  // Add another destination mapping to exiting data.
908  backRefDataIter->second = searchIter->second;
909  }
910 }
911 
912 } // namespace PackedMemory
913 } // namespace Teamcenter
914 
915 #endif // PACKED_MEMORY_BUILDER_HXX