//-***************************************************************************** // Same as above, only this time for the files. // Alembic uses LittleEndian by default. hid_t GetFileH5T( const AbcA::DataType &adt, bool &oCleanUp ) { oCleanUp = false; hid_t baseDtype = -1; switch ( adt.getPod() ) { case kBooleanPOD: oCleanUp = true; baseDtype = GetFileBoolH5T(); break; case kUint8POD: baseDtype = H5T_STD_U8LE; break; case kInt8POD: baseDtype = H5T_STD_I8LE; break; case kUint16POD: baseDtype = H5T_STD_U16LE; break; case kInt16POD: baseDtype = H5T_STD_I16LE; break; case kUint32POD: baseDtype = H5T_STD_U32LE; break; case kInt32POD: baseDtype = H5T_STD_I32LE; break; case kUint64POD: baseDtype = H5T_STD_U64LE; break; case kInt64POD: baseDtype = H5T_STD_I64LE; break; case kFloat16POD: oCleanUp = true; baseDtype = GetFileHalfH5T(); break; case kFloat32POD: baseDtype = H5T_IEEE_F32LE; break; case kFloat64POD: baseDtype = H5T_IEEE_F64LE; break; default: ABCA_THROW( "Unsuppored POD type: " << PODName( adt.getPod() ) ); } ABCA_ASSERT( baseDtype >= 0, "Bad base datatype id" ); return baseDtype; }
//-***************************************************************************** //-***************************************************************************** //-***************************************************************************** // SUPPORT FUNCTIONS //-***************************************************************************** //-***************************************************************************** //-***************************************************************************** // In order to read or write a slab of a given DataType, we need an H5::Datatype // for the dataset in the file, and a 'native' one for how it will be // stored in local memory. We are not bothering with named datatypes for now. // Though, it may prove timesaving later on. // // So - we need some sort of structure which allows you to take an // AlembicAsset::DataType and come up with an H5::Datatype for it. hid_t GetNativeH5T( const AbcA::DataType &adt, bool &oCleanUp ) { oCleanUp = false; hid_t baseDtype = -1; switch ( adt.getPod() ) { case kBooleanPOD: oCleanUp = true; baseDtype = GetNativeBoolH5T(); break; case kUint8POD: baseDtype = H5T_NATIVE_UINT8; break; case kInt8POD: baseDtype = H5T_NATIVE_INT8; break; case kUint16POD: baseDtype = H5T_NATIVE_UINT16; break; case kInt16POD: baseDtype = H5T_NATIVE_INT16; break; case kUint32POD: baseDtype = H5T_NATIVE_UINT32; break; case kInt32POD: baseDtype = H5T_NATIVE_INT32; break; case kUint64POD: baseDtype = H5T_NATIVE_UINT64; break; case kInt64POD: baseDtype = H5T_NATIVE_INT64; break; case kFloat16POD: oCleanUp = true; baseDtype = GetNativeHalfH5T(); break; case kFloat32POD: baseDtype = H5T_NATIVE_FLOAT; break; case kFloat64POD: baseDtype = H5T_NATIVE_DOUBLE; break; default: ABCA_THROW( "Unsuppored POD type: " << PODName( adt.getPod() ) ); } ABCA_ASSERT( baseDtype >= 0, "Bad base datatype id" ); return baseDtype; }
//-***************************************************************************** AbcA::ArrayPropertyWriterPtr CpwData::createArrayProperty( AbcA::CompoundPropertyWriterPtr iParent, const std::string & iName, const AbcA::MetaData & iMetaData, const AbcA::DataType & iDataType, Util::uint32_t iTimeSamplingIndex ) { if ( m_madeProperties.count( iName ) ) { ABCA_THROW( "Already have a property named: " << iName ); } ABCA_ASSERT( iDataType.getExtent() != 0 && iDataType.getPod() != Alembic::Util::kNumPlainOldDataTypes && iDataType.getPod() != Alembic::Util::kUnknownPOD, "createArrayProperty, illegal DataType provided."); // will assert if TimeSamplingPtr not found AbcA::TimeSamplingPtr ts = iParent->getObject()->getArchive()->getTimeSampling( iTimeSamplingIndex ); PropertyHeaderPtr headerPtr( new PropertyHeaderAndFriends( iName, AbcA::kArrayProperty, iMetaData, iDataType, ts, iTimeSamplingIndex ) ); Alembic::Util::shared_ptr<ApwImpl> ret( new ApwImpl( iParent, m_group->addGroup(), headerPtr, m_propertyHeaders.size() ) ); m_propertyHeaders.push_back( headerPtr ); m_madeProperties[iName] = WeakBpwPtr( ret ); m_hashes.push_back(0); m_hashes.push_back(0); return ret; }
//-***************************************************************************** void WritePropertyInfo( hid_t iGroup, const std::string &iName, AbcA::PropertyType iPropertyType, const AbcA::DataType &iDataType, bool isScalarLike, uint32_t iTimeSamplingIndex, uint32_t iNumSamples, uint32_t iFirstChangedIndex, uint32_t iLastChangedIndex ) { uint32_t info[5] = {0, 0, 0, 0, 0}; uint32_t numFields = 1; static const uint32_t ptypeMask = ( uint32_t )BOOST_BINARY ( 0000 0000 0000 0000 0000 0000 0000 0011 ); static const uint32_t podMask = ( uint32_t )BOOST_BINARY ( 0000 0000 0000 0000 0000 0000 0011 1100 ); static const uint32_t hasTsidxMask = ( uint32_t )BOOST_BINARY ( 0000 0000 0000 0000 0000 0000 0100 0000 ); static const uint32_t noRepeatsMask = ( uint32_t )BOOST_BINARY ( 0000 0000 0000 0000 0000 0000 1000 0000 ); static const uint32_t extentMask = ( uint32_t )BOOST_BINARY( 0000 0000 0000 0000 1111 1111 0000 0000 ); // for compounds we just write out 0 if ( iPropertyType != AbcA::kCompoundProperty ) { // Slam the property type in there. info[0] |= ptypeMask & ( uint32_t )iPropertyType; // arrays may be scalar like, scalars are already scalar like info[0] |= ( uint32_t ) isScalarLike; uint32_t pod = ( uint32_t )iDataType.getPod(); info[0] |= podMask & ( pod << 2 ); if (iTimeSamplingIndex != 0) { info[0] |= hasTsidxMask; } if (iFirstChangedIndex == 1 && iLastChangedIndex == iNumSamples - 1) { info[0] |= noRepeatsMask; } uint32_t extent = ( uint32_t )iDataType.getExtent(); info[0] |= extentMask & ( extent << 8 ); ABCA_ASSERT( iFirstChangedIndex <= iNumSamples && iLastChangedIndex <= iNumSamples && iFirstChangedIndex <= iLastChangedIndex, "Illegal Sampling!" << std::endl << "Num Samples: " << iNumSamples << std::endl << "First Changed Index: " << iFirstChangedIndex << std::endl << "Last Changed Index: " << iLastChangedIndex << std::endl ); // Write the num samples. Only bother writing if // the num samples is greater than 1. Existence of name.smp0 // is used by the reader to determine if 0 or 1 sample. if ( iNumSamples > 1 ) { info[1] = iNumSamples; numFields ++; if ( iFirstChangedIndex > 1 || ( iLastChangedIndex != 0 && iLastChangedIndex != iNumSamples - 1 ) ) { info[2] = iFirstChangedIndex; info[3] = iLastChangedIndex; numFields += 2; } } // finally set time sampling index on the end if necessary if (iTimeSamplingIndex != 0) { info[numFields] = iTimeSamplingIndex; numFields ++; } } WriteSmallArray( iGroup, iName + ".info", H5T_STD_U32LE, H5T_NATIVE_UINT32, numFields, ( const void * ) info ); }
//-***************************************************************************** void ReadArray( void * iIntoLocation, hid_t iParent, const std::string &iName, const AbcA::DataType &iDataType, hid_t iType ) { // Dispatch string stuff. if ( iDataType.getPod() == kStringPOD ) { return ReadStringArray( iIntoLocation, iParent, iName, iDataType ); } else if ( iDataType.getPod() == kWstringPOD ) { return ReadWstringArray( iIntoLocation, iParent, iName, iDataType ); } assert( iDataType.getPod() != kStringPOD && iDataType.getPod() != kWstringPOD ); // Open the data set. hid_t dsetId = H5Dopen( iParent, iName.c_str(), H5P_DEFAULT ); ABCA_ASSERT( dsetId >= 0, "Cannot open dataset: " << iName ); DsetCloser dsetCloser( dsetId ); // Read the data space. hid_t dspaceId = H5Dget_space( dsetId ); ABCA_ASSERT( dspaceId >= 0, "Could not get dataspace for dataSet: " << iName ); DspaceCloser dspaceCloser( dspaceId ); // Read the data type. hid_t dtypeId = H5Dget_type( dsetId ); ABCA_ASSERT( dtypeId >= 0, "Could not get datatype for dataSet: " << iName ); DtypeCloser dtypeCloser( dtypeId ); H5S_class_t dspaceClass = H5Sget_simple_extent_type( dspaceId ); if ( dspaceClass == H5S_SIMPLE ) { // Get the dimensions int rank = H5Sget_simple_extent_ndims( dspaceId ); ABCA_ASSERT( rank == 1, "H5Sget_simple_extent_ndims() must be 1." ); hsize_t hdim = 0; rank = H5Sget_simple_extent_dims( dspaceId, &hdim, NULL ); ABCA_ASSERT( hdim > 0, "Degenerate dims in Dataset read" ); // And... read into it. herr_t status = H5Dread( dsetId, iType, H5S_ALL, H5S_ALL, H5P_DEFAULT, iIntoLocation ); ABCA_ASSERT( status >= 0, "H5Dread() failed." ); } else if ( dspaceClass != H5S_NULL ) { ABCA_THROW( "Unexpected scalar dataspace encountered." ); } }
//-***************************************************************************** AbcA::ArraySamplePtr ReadArray( AbcA::ReadArraySampleCachePtr iCache, hid_t iParent, const std::string &iName, const AbcA::DataType &iDataType, hid_t iFileType, hid_t iNativeType ) { // Dispatch string stuff. if ( iDataType.getPod() == kStringPOD ) { return ReadStringArray( iCache, iParent, iName, iDataType ); } else if ( iDataType.getPod() == kWstringPOD ) { return ReadWstringArray( iCache, iParent, iName, iDataType ); } assert( iDataType.getPod() != kStringPOD && iDataType.getPod() != kWstringPOD ); // Open the data set. hid_t dsetId = H5Dopen( iParent, iName.c_str(), H5P_DEFAULT ); ABCA_ASSERT( dsetId >= 0, "Cannot open dataset: " << iName ); DsetCloser dsetCloser( dsetId ); // Read the data space. hid_t dspaceId = H5Dget_space( dsetId ); ABCA_ASSERT( dspaceId >= 0, "Could not get dataspace for dataSet: " << iName ); DspaceCloser dspaceCloser( dspaceId ); AbcA::ArraySample::Key key; bool foundDigest = false; // if we are caching, get the key and see if it is being used if ( iCache ) { key.origPOD = iDataType.getPod(); key.readPOD = key.origPOD; key.numBytes = Util::PODNumBytes( key.readPOD ) * H5Sget_simple_extent_npoints( dspaceId ); foundDigest = ReadKey( dsetId, "key", key ); AbcA::ReadArraySampleID found = iCache->find( key ); if ( found ) { AbcA::ArraySamplePtr ret = found.getSample(); assert( ret ); if ( ret->getDataType().getPod() != iDataType.getPod() ) { ABCA_THROW( "ERROR: Read data type for dset: " << iName << ": " << ret->getDataType() << " does not match expected data type: " << iDataType ); } // Got it! return ret; } } // Okay, we haven't found it in a cache. // Read the data type. hid_t dtypeId = H5Dget_type( dsetId ); ABCA_ASSERT( dtypeId >= 0, "Could not get datatype for dataSet: " << iName ); DtypeCloser dtypeCloser( dtypeId ); ABCA_ASSERT( EquivalentDatatypes( iFileType, dtypeId ), "File DataType clash for array dataset: " << iName ); AbcA::ArraySamplePtr ret; H5S_class_t dspaceClass = H5Sget_simple_extent_type( dspaceId ); if ( dspaceClass == H5S_SIMPLE ) { // Get the dimensions int rank = H5Sget_simple_extent_ndims( dspaceId ); ABCA_ASSERT( rank == 1, "H5Sget_simple_extent_ndims() must be 1." ); hsize_t hdim = 0; rank = H5Sget_simple_extent_dims( dspaceId, &hdim, NULL ); Dimensions dims; std::string dimName = iName + ".dims"; if ( H5Aexists( iParent, dimName.c_str() ) ) { ReadDimensions( iParent, dimName, dims ); } else { dims.setRank(1); dims[0] = hdim / iDataType.getExtent(); } ABCA_ASSERT( dims.numPoints() > 0, "Degenerate dims in Dataset read" ); // Create a buffer into which we shall read. ret = AbcA::AllocateArraySample( iDataType, dims ); assert( ret->getData() ); // And... read into it. herr_t status = H5Dread( dsetId, iNativeType, H5S_ALL, H5S_ALL, H5P_DEFAULT, const_cast<void*>( ret->getData() ) ); ABCA_ASSERT( status >= 0, "H5Dread() failed." ); } else if ( dspaceClass == H5S_NULL ) { Dimensions dims; std::string dimName = iName + ".dims"; if ( H5Aexists( iParent, dimName.c_str() ) ) { ReadDimensions( iParent, dimName, dims ); ABCA_ASSERT( dims.rank() > 0, "Degenerate rank in Dataset read" ); // Num points should be zero here. ABCA_ASSERT( dims.numPoints() == 0, "Expecting zero points in dimensions" ); } else { dims.setRank(1); dims[0] = 0; } ret = AbcA::AllocateArraySample( iDataType, dims ); } else { ABCA_THROW( "Unexpected scalar dataspace encountered." ); } // Store if there is a cache. if ( foundDigest && iCache ) { AbcA::ReadArraySampleID stored = iCache->store( key, ret ); if ( stored ) { return stored.getSample(); } } // Otherwise, just leave! ArraySamplePtr returned by AllocateArraySample // already has fancy-dan deleter built in. // I REALLY LOVE SMART PTRS. return ret; }