| // Copyright 2026 Google LLC | |
| // | |
| // Licensed under the Apache License, Version 2.0 (the "License"); | |
| // you may not use this file except in compliance with the License. | |
| // You may obtain a copy of the License at | |
| // | |
| // https://www.apache.org/licenses/LICENSE-2.0 | |
| // | |
| // Unless required by applicable law or agreed to in writing, software | |
| // distributed under the License is distributed on an "AS IS" BASIS, | |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| // See the License for the specific language governing permissions and | |
| // limitations under the License. | |
| // | |
| #include "formats/tiff/hasher.h" | |
| #include <algorithm> | |
| #include <cstdint> | |
| #include <string> | |
| #include "absl/status/status.h" | |
| #include "absl/status/status_macros.h" | |
| #include "absl/status/statusor.h" | |
| #include "bindings/input_hasher.h" | |
| #include "riegeli/bytes/reader.h" | |
| #include "riegeli/bytes/writer.h" | |
| #include "riegeli/endian/endian_writing.h" | |
| namespace credentio { | |
| namespace { | |
| // It's fine to use a fixed kMaxChunkBytes here, because the embedder will work | |
| // with any size greater than 8 bytes. (Larger sizes are more efficient.) | |
| constexpr uint64_t kMaxChunkBytes = 1048576; // 1 MiB | |
| template <typename T> | |
| absl::StatusOr<std::string> UintToString(T value, bool is_big_endian) { | |
| std::string buffer(sizeof(T), '\0'); | |
| if constexpr (sizeof(T) == 2 || sizeof(T) == 4) { | |
| if (is_big_endian) { | |
| riegeli::WriteBigEndian<T>(value, buffer.data()); | |
| } else { | |
| riegeli::WriteLittleEndian<T>(value, buffer.data()); | |
| } | |
| } else { | |
| return absl::InvalidArgumentError("Unsupported type size"); | |
| } | |
| return buffer; | |
| } | |
| template <typename T> | |
| absl::Status HashWrite(T value, bool is_big_endian, InputHasher& hasher, | |
| riegeli::Writer& writer) { | |
| ABSL_ASSIGN_OR_RETURN(std::string buffer, UintToString(value, is_big_endian)); | |
| hasher.Update(buffer); | |
| if (!writer.Write(buffer)) { | |
| return absl::DataLossError("failed to write to writer"); | |
| } | |
| return absl::OkStatus(); | |
| } | |
| template <typename T> | |
| absl::Status Write(T value, bool is_big_endian, riegeli::Writer& writer) { | |
| ABSL_ASSIGN_OR_RETURN(std::string buffer, UintToString(value, is_big_endian)); | |
| if (!writer.Write(buffer)) { | |
| return absl::DataLossError("failed to write to writer"); | |
| } | |
| return absl::OkStatus(); | |
| } | |
| } // namespace | |
| absl::Status ReadHash(riegeli::Reader& reader, int64_t length, | |
| InputHasher& hasher) { | |
| uint64_t remaining_length_to_copy = length; | |
| while (remaining_length_to_copy > 0) { | |
| std::string chunk; | |
| auto chunk_size = std::min(remaining_length_to_copy, kMaxChunkBytes); | |
| if (!reader.Read(chunk_size, chunk)) { | |
| return absl::DataLossError("Failed to read from source TIFF"); | |
| } | |
| hasher.Update(chunk); | |
| remaining_length_to_copy -= chunk_size; | |
| } | |
| return absl::OkStatus(); | |
| } | |
| absl::Status ReadHashWrite(riegeli::Reader& reader, int64_t length, | |
| InputHasher& hasher, riegeli::Writer& writer) { | |
| uint64_t remaining_length_to_copy = length; | |
| while (remaining_length_to_copy > 0) { | |
| std::string chunk; | |
| auto chunk_size = std::min(remaining_length_to_copy, kMaxChunkBytes); | |
| if (!reader.Read(chunk_size, chunk)) { | |
| return absl::DataLossError("Failed to read from source TIFF"); | |
| } | |
| hasher.Update(chunk); | |
| if (!writer.Write(chunk)) { | |
| return absl::DataLossError("Failed to write to destination TIFF"); | |
| } | |
| remaining_length_to_copy -= chunk_size; | |
| } | |
| return absl::OkStatus(); | |
| } | |
| absl::Status HashWrite16(uint16_t value, bool is_big_endian, | |
| InputHasher& hasher, riegeli::Writer& writer) { | |
| return HashWrite<uint16_t>(value, is_big_endian, hasher, writer); | |
| } | |
| absl::Status HashWrite32(uint32_t value, bool is_big_endian, | |
| InputHasher& hasher, riegeli::Writer& writer) { | |
| return HashWrite<uint32_t>(value, is_big_endian, hasher, writer); | |
| } | |
| absl::Status Write32(uint32_t value, bool is_big_endian, | |
| riegeli::Writer& writer) { | |
| return Write<uint32_t>(value, is_big_endian, writer); | |
| } | |
| } // namespace credentio |