XRefTable.h 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144
  1. /*
  2. * Copyright (c) 2021-2022, Matthew Olsson <mattco@serenityos.org>
  3. *
  4. * SPDX-License-Identifier: BSD-2-Clause
  5. */
  6. #pragma once
  7. #include <AK/Format.h>
  8. #include <AK/RefCounted.h>
  9. #include <AK/String.h>
  10. #include <AK/Vector.h>
  11. namespace PDF {
  12. constexpr long invalid_byte_offset = NumericLimits<long>::max();
  13. struct XRefEntry {
  14. long byte_offset { invalid_byte_offset };
  15. u16 generation_number { 0 };
  16. bool in_use { false };
  17. bool compressed { false };
  18. };
  19. struct XRefSection {
  20. int starting_index;
  21. int count;
  22. Vector<XRefEntry> entries;
  23. };
  24. class XRefTable final : public RefCounted<XRefTable> {
  25. public:
  26. PDFErrorOr<void> merge(XRefTable&& other)
  27. {
  28. auto this_size = m_entries.size();
  29. auto other_size = other.m_entries.size();
  30. m_entries.ensure_capacity(other_size);
  31. for (size_t i = 0; i < other_size; i++) {
  32. auto other_entry = other.m_entries[i];
  33. if (i >= this_size) {
  34. m_entries.unchecked_append(other_entry);
  35. continue;
  36. }
  37. auto this_entry = m_entries[i];
  38. if (this_entry.byte_offset == invalid_byte_offset) {
  39. m_entries[i] = other_entry;
  40. } else if (other_entry.byte_offset != invalid_byte_offset) {
  41. // Both xref tables have an entry for the same object index
  42. return Error { Error::Type::Parse, "Conflicting xref entry during merge" };
  43. }
  44. }
  45. return {};
  46. }
  47. void add_section(XRefSection const& section)
  48. {
  49. m_entries.ensure_capacity(section.starting_index + section.count);
  50. for (int i = static_cast<int>(m_entries.size()); i < section.starting_index; i++)
  51. m_entries.append(XRefEntry {});
  52. for (auto& entry : section.entries)
  53. m_entries.append(entry);
  54. }
  55. [[nodiscard]] ALWAYS_INLINE bool has_object(size_t index) const
  56. {
  57. return index < m_entries.size() && m_entries[index].byte_offset != -1;
  58. }
  59. [[nodiscard]] ALWAYS_INLINE long byte_offset_for_object(size_t index) const
  60. {
  61. VERIFY(has_object(index));
  62. return m_entries[index].byte_offset;
  63. }
  64. [[nodiscard]] ALWAYS_INLINE long object_stream_for_object(size_t index) const
  65. {
  66. return byte_offset_for_object(index);
  67. }
  68. [[nodiscard]] ALWAYS_INLINE u16 generation_number_for_object(size_t index) const
  69. {
  70. VERIFY(has_object(index));
  71. return m_entries[index].generation_number;
  72. }
  73. [[nodiscard]] ALWAYS_INLINE u16 object_stream_index_for_object(size_t index) const
  74. {
  75. return generation_number_for_object(index);
  76. }
  77. [[nodiscard]] ALWAYS_INLINE bool is_object_in_use(size_t index) const
  78. {
  79. VERIFY(has_object(index));
  80. return m_entries[index].in_use;
  81. }
  82. [[nodiscard]] ALWAYS_INLINE bool is_object_compressed(size_t index) const
  83. {
  84. VERIFY(has_object(index));
  85. return m_entries[index].compressed;
  86. }
  87. private:
  88. friend struct AK::Formatter<PDF::XRefTable>;
  89. Vector<XRefEntry> m_entries;
  90. };
  91. }
  92. namespace AK {
  93. template<>
  94. struct Formatter<PDF::XRefEntry> : Formatter<StringView> {
  95. ErrorOr<void> format(FormatBuilder& builder, PDF::XRefEntry const& entry)
  96. {
  97. return Formatter<StringView>::format(builder,
  98. String::formatted("XRefEntry {{ offset={} generation={} used={} }}",
  99. entry.byte_offset,
  100. entry.generation_number,
  101. entry.in_use));
  102. }
  103. };
  104. template<>
  105. struct Formatter<PDF::XRefTable> : Formatter<StringView> {
  106. ErrorOr<void> format(FormatBuilder& format_builder, PDF::XRefTable const& table)
  107. {
  108. StringBuilder builder;
  109. builder.append("XRefTable {"sv);
  110. for (auto& entry : table.m_entries)
  111. builder.appendff("\n {}", entry);
  112. builder.append("\n}"sv);
  113. return Formatter<StringView>::format(format_builder, builder.to_string());
  114. }
  115. };
  116. }