Segment.fs 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. namespace FSharp.Data.Tdms
  2. open System
  3. open System.Buffers
  4. open System.IO
  5. open System.Numerics
  6. open System.Runtime.InteropServices
  7. type Tag =
  8. | Tdsm = 1834173524u
  9. | Tdsh = 1750287444u
  10. [<Flags>]
  11. type TableOfContents =
  12. | ContainsMetaData = 2u
  13. | ContainsRawData = 8u
  14. | ContainsDaqMxRawData = 128u
  15. | ContainsInterleavedData = 32u
  16. | ContainsBigEndianData = 64u
  17. | ContainsNewObjectList = 4u
  18. type Version =
  19. | ``1.0`` = 4712u
  20. | ``2.0`` = 4713u
  21. [<Struct>]
  22. type LeadIn =
  23. { Tag: Tag
  24. TableOfContents: TableOfContents
  25. Version: Version
  26. NextSegmentOffset: uint64
  27. RawDataOffset: uint64 }
  28. module Segment =
  29. let readLeadIn (buffer: byte ReadOnlySpan byref) =
  30. let tag =
  31. Buffer.readUInt &buffer false
  32. |> LanguagePrimitives.EnumOfValue<uint, Tag>
  33. let tableOfContents =
  34. Buffer.readUInt &buffer false
  35. |> LanguagePrimitives.EnumOfValue<uint, TableOfContents>
  36. let bigEndian =
  37. tableOfContents.HasFlag(TableOfContents.ContainsBigEndianData)
  38. { Tag = tag
  39. TableOfContents = tableOfContents
  40. Version =
  41. Buffer.readUInt &buffer bigEndian
  42. |> LanguagePrimitives.EnumOfValue<uint, Version>
  43. NextSegmentOffset = Buffer.readUInt64 &buffer bigEndian
  44. RawDataOffset = Buffer.readUInt64 &buffer bigEndian }
  45. let readPropertyValue (buffer: byte ReadOnlySpan byref) bigEndian propertyType =
  46. if propertyType = typeof<unit> then
  47. buffer <- buffer.Slice 1
  48. box ()
  49. elif propertyType = typeof<bool> then
  50. let value = box (buffer.[0] <> 0uy)
  51. buffer <- buffer.Slice 1
  52. value
  53. elif propertyType = typeof<int8> then
  54. let value =
  55. box (MemoryMarshal.Cast<uint8, int8> buffer).[0]
  56. buffer <- buffer.Slice 1
  57. value
  58. elif propertyType = typeof<int16> then
  59. Buffer.readInt16 &buffer bigEndian |> box
  60. elif propertyType = typeof<int> then
  61. Buffer.readInt &buffer bigEndian |> box
  62. elif propertyType = typeof<int64> then
  63. Buffer.readInt64 &buffer bigEndian |> box
  64. elif propertyType = typeof<uint8> then
  65. let value = box buffer.[0]
  66. buffer <- buffer.Slice 1
  67. value
  68. elif propertyType = typeof<uint16> then
  69. Buffer.readUInt16 &buffer bigEndian |> box
  70. elif propertyType = typeof<uint> then
  71. Buffer.readUInt &buffer bigEndian |> box
  72. elif propertyType = typeof<uint64> then
  73. Buffer.readUInt64 &buffer bigEndian |> box
  74. elif propertyType = typeof<float32> then
  75. Buffer.readFloat32 &buffer bigEndian |> box
  76. elif propertyType = typeof<float> then
  77. Buffer.readFloat &buffer bigEndian |> box
  78. elif propertyType = typeof<struct (float32 * float32)> then
  79. struct (Buffer.readFloat32 &buffer bigEndian |> float, Buffer.readFloat32 &buffer bigEndian |> float)
  80. |> box
  81. elif propertyType = typeof<Complex> then
  82. Complex(Buffer.readFloat &buffer bigEndian, Buffer.readFloat &buffer bigEndian)
  83. |> box
  84. elif propertyType = typeof<float80> then
  85. Buffer.readFloat80 &buffer bigEndian |> box
  86. elif propertyType = typeof<Timestamp> then
  87. (if bigEndian then
  88. { SecondsSinceNiEpoch = Buffer.readInt64 &buffer bigEndian
  89. FractionsOfASecond = Buffer.readUInt64 &buffer bigEndian }
  90. else
  91. { FractionsOfASecond = Buffer.readUInt64 &buffer bigEndian
  92. SecondsSinceNiEpoch = Buffer.readInt64 &buffer bigEndian })
  93. |> box
  94. elif propertyType = typeof<string> then
  95. Buffer.readString &buffer bigEndian |> box
  96. else
  97. failwithf "Property type not implemented: %A" propertyType
  98. let createObject name (objects: _ ResizeArray) bigEndian =
  99. match Seq.tryFind (fun object -> object.Name = name) objects with
  100. | Some object -> object
  101. | None ->
  102. let object =
  103. { Name = name
  104. BigEndian = bigEndian
  105. RawDataBlocks = None
  106. Properties = ResizeArray() }
  107. objects.Add object
  108. object
  109. let readMetaData objects rawDataOffset nextSegmentOffset (buffer: _ byref) bigEndian interleaved =
  110. let objectCount = Buffer.readInt &buffer bigEndian
  111. let newOrUpdatedObjects = Array.zeroCreate objectCount
  112. let objectsWithRawData = ResizeArray()
  113. let mutable rawDataPosition = rawDataOffset
  114. for i = 0 to objectCount - 1 do
  115. let object =
  116. createObject (Buffer.readString &buffer bigEndian) objects bigEndian
  117. newOrUpdatedObjects.[i] <- object
  118. let rawDataSkip =
  119. Object.readRawDataIndex object rawDataPosition &buffer bigEndian interleaved
  120. rawDataPosition <- rawDataPosition + rawDataSkip
  121. if rawDataSkip > 0uL then
  122. objectsWithRawData.Add object
  123. let propertyCount = Buffer.readUInt &buffer bigEndian |> int
  124. for j = 0 to propertyCount - 1 do
  125. let propertyName = Buffer.readString &buffer bigEndian
  126. let propertyType = Buffer.readType &buffer bigEndian
  127. let propertyValue =
  128. readPropertyValue &buffer bigEndian propertyType
  129. let property =
  130. { Name = propertyName
  131. Type = propertyType
  132. Raw = propertyValue }
  133. match Seq.tryFindIndex (fun (property: Property) -> property.Name = propertyName) object.Properties with
  134. | None -> object.Properties.Add property
  135. | Some index -> object.Properties.[index] <- property
  136. let sizes, rawDataBlocksToUpdate =
  137. Seq.choose
  138. (fun ({ RawDataBlocks = rawDataBlocks }: FSharp.Data.Tdms.Object) ->
  139. Option.bind
  140. (fun rawDataBlocks' ->
  141. match rawDataBlocks' with
  142. | PrimitiveRawDataBlocks (ty, primitiveRawDataBlockArray) ->
  143. Seq.tryLast primitiveRawDataBlockArray
  144. |> Option.map
  145. (function
  146. | DecimatedPrimitiveRawDataBlock (start, count) ->
  147. uint64 (Marshal.SizeOf ty) * count, rawDataBlocks'
  148. | InterleavedPrimitiveRawDataBlock { Start = start; Count = count } ->
  149. uint64 (Marshal.SizeOf ty) * count, rawDataBlocks')
  150. | StringRawDataBlocks stringRawDataBlockArray ->
  151. Seq.tryLast stringRawDataBlockArray
  152. |> Option.map (fun (_, _, bytes) -> bytes, rawDataBlocks'))
  153. rawDataBlocks)
  154. objectsWithRawData
  155. |> Seq.toArray
  156. |> Array.unzip
  157. let chunkSize = Array.sum sizes
  158. if chunkSize > 0uL then
  159. let chunkOffsets =
  160. [ chunkSize .. chunkSize .. (nextSegmentOffset - rawDataOffset) - chunkSize ]
  161. for rawDataBlocks in rawDataBlocksToUpdate do
  162. match rawDataBlocks with
  163. | PrimitiveRawDataBlocks (_, primitiveRawDataBlockArray) ->
  164. Seq.tryLast primitiveRawDataBlockArray
  165. |> Option.iter
  166. (function
  167. | DecimatedPrimitiveRawDataBlock (start, count) ->
  168. primitiveRawDataBlockArray.AddRange(
  169. List.map
  170. (fun chunkOffset -> DecimatedPrimitiveRawDataBlock(start + chunkOffset, count))
  171. chunkOffsets
  172. )
  173. | InterleavedPrimitiveRawDataBlock ({ Start = start } as block) ->
  174. primitiveRawDataBlockArray.AddRange(
  175. List.map
  176. (fun chunkOffset ->
  177. InterleavedPrimitiveRawDataBlock
  178. { block with
  179. Start = start + chunkOffset })
  180. chunkOffsets
  181. ))
  182. | StringRawDataBlocks stringRawDataBlockArray ->
  183. Seq.tryLast stringRawDataBlockArray
  184. |> Option.iter
  185. (fun (start, count, bytes) ->
  186. stringRawDataBlockArray.AddRange(
  187. List.map (fun chunkOffset -> start + chunkOffset, count, bytes) chunkOffsets
  188. ))
  189. if interleaved then
  190. Array.iter
  191. (fun ({ RawDataBlocks = rawDataBlocks }: FSharp.Data.Tdms.Object) ->
  192. match rawDataBlocks with
  193. | None -> ()
  194. | Some (StringRawDataBlocks _) -> ()
  195. | Some (PrimitiveRawDataBlocks (ty, primitiveRawDataBlocksArray)) ->
  196. Seq.tryLast primitiveRawDataBlocksArray
  197. |> Option.iter
  198. (function
  199. | DecimatedPrimitiveRawDataBlock _ -> ()
  200. | InterleavedPrimitiveRawDataBlock interleavedPrimitiveRawDataBlock ->
  201. interleavedPrimitiveRawDataBlock.Skip <-
  202. (rawDataPosition - rawDataOffset)
  203. - uint64 (Marshal.SizeOf ty)))
  204. newOrUpdatedObjects
  205. let tdsh =
  206. [| 0x54uy
  207. 0x44uy
  208. 0x53uy
  209. 0x68uy |]