private AvifItemData ReadDataFromMultipleExtents(ItemLocationEntry entry) { AvifItemData data; IReadOnlyList <ItemLocationExtent> extents = entry.Extents; ulong totalItemSize = entry.TotalItemSize; if (totalItemSize <= ManagedAvifItemDataMaxSize) { ManagedAvifItemData managedItemData = new ManagedAvifItemData((int)totalItemSize, this.arrayPool); int offset = 0; int remainingBytes = (int)managedItemData.Length; byte[] bytes = managedItemData.GetBuffer(); for (int i = 0; i < extents.Count; i++) { ItemLocationExtent extent = extents[i]; long itemOffset = CalculateExtentOffset(entry.BaseOffset, entry.ConstructionMethod, extent); int length = (int)extent.Length; if (length > remainingBytes) { throw new FormatException("The extent length is greater than the number of bytes remaining for the item."); } this.reader.Position = itemOffset; this.reader.ProperRead(bytes, offset, length); offset += length; remainingBytes -= length; } if (remainingBytes > 0) { // This should never happen, the total item size is the sum of all the extent sizes. throw new FormatException("The item has more data than was read from the extents."); } data = managedItemData; } else { UnmanagedAvifItemData unmanagedItemData = new UnmanagedAvifItemData(totalItemSize); try { ulong offset = 0; ulong remainingBytes = totalItemSize; for (int i = 0; i < extents.Count; i++) { ItemLocationExtent extent = extents[i]; long itemOffset = CalculateExtentOffset(entry.BaseOffset, entry.ConstructionMethod, extent); ulong length = extent.Length; if (length > remainingBytes) { throw new FormatException("The extent length is greater than the number of bytes remaining for the item."); } this.reader.Position = itemOffset; this.reader.ProperRead(unmanagedItemData.UnmanagedBuffer, offset, length); offset += length; remainingBytes -= length; } if (remainingBytes > 0) { // This should never happen, the total item size is the sum of all the extent sizes. throw new FormatException("The item has more data than was read from the extents."); } data = unmanagedItemData; unmanagedItemData = null; } finally { unmanagedItemData?.Dispose(); } } return(data); }
private long CalculateExtentOffset(ulong baseOffset, ConstructionMethod constructionMethod, ItemLocationExtent extent) { if (extent is null) { ExceptionUtil.ThrowArgumentNullException(nameof(extent)); } ulong offset; try { if (constructionMethod == ConstructionMethod.FileOffset) { checked { offset = baseOffset + extent.Offset; if ((offset + extent.Length) > this.fileLength) { throw new FormatException("The item has an invalid file offset."); } } } else if (constructionMethod == ConstructionMethod.IDatBoxOffset) { ItemDataBox dataBox = this.metaBox.ItemData; if (dataBox is null) { throw new FormatException("The file does not have an item data box."); } checked { if ((extent.Offset + extent.Length) > (ulong)dataBox.Length) { throw new FormatException("The item has an invalid data box offset."); } offset = (ulong)dataBox.Offset + extent.Offset; if ((offset + extent.Length) > this.fileLength) { throw new FormatException("The item has an invalid file offset."); } } } else { throw new FormatException($"ItemLocationEntry construction method { constructionMethod } is not supported."); } } catch (OverflowException ex) { throw new FormatException("Overflow when attempting to calculate the item file offset.", ex); } if (offset > long.MaxValue) { throw new FormatException($"The item file offset exceeds {long.MaxValue:F0} bytes."); } return((long)offset); }