Beispiel #1
0
uint64 EbmlVoid::ReplaceWith(EbmlElement & EltToReplaceWith, IOCallback & output, bool ComeBackAfterward, bool bWithDefault)
{
	EltToReplaceWith.UpdateSize(bWithDefault);
	if (HeadSize() + GetSize() < EltToReplaceWith.GetSize() + EltToReplaceWith.HeadSize()) {
		// the element can't be written here !
		return INVALID_FILEPOS_T;
	}
	if (HeadSize() + GetSize() - EltToReplaceWith.GetSize() - EltToReplaceWith.HeadSize() == 1) {
		// there is not enough space to put a filling element
		return INVALID_FILEPOS_T;
	}

	uint64 CurrentPosition = output.getFilePointer();

	output.setFilePointer(GetElementPosition());
	EltToReplaceWith.Render(output, bWithDefault);

	if (HeadSize() + GetSize() - EltToReplaceWith.GetSize() - EltToReplaceWith.HeadSize() > 1) {
	  // fill the rest with another void element
	  EbmlVoid aTmp;
	  aTmp.SetSize_(HeadSize() + GetSize() - EltToReplaceWith.GetSize() - EltToReplaceWith.HeadSize() - 1); // 1 is the length of the Void ID
	  int HeadBefore = aTmp.HeadSize();
	  aTmp.SetSize_(aTmp.GetSize() - CodedSizeLength(aTmp.GetSize(), aTmp.GetSizeLength(), aTmp.IsFiniteSize()));
	  int HeadAfter = aTmp.HeadSize();
	  if (HeadBefore != HeadAfter) {
		  aTmp.SetSizeLength(CodedSizeLength(aTmp.GetSize(), aTmp.GetSizeLength(), aTmp.IsFiniteSize()) - (HeadAfter - HeadBefore));
	  }
	  aTmp.RenderHead(output, false, bWithDefault); // the rest of the data is not rewritten
	}

	if (ComeBackAfterward) {
		output.setFilePointer(CurrentPosition);
	}

	return GetSize() + HeadSize();
}
/** \brief Create an EbmlVoid element at a specific location

    This function fills a gap in the file with an EbmlVoid. If an
    EbmlVoid element is located directly behind the gap then this
    element is overwritten as well.

    The function calculates the size of the new void element by taking
    the next non-EbmlVoid's position and subtracting from it the end
    position of the current element indicated by the \c data_idx
    parameter.

    If the space is not big enough to contain an EbmlVoid element then
    the EBML head of the following element is moved one byte to the
    front and its size field is extended by one byte. That way the
    file stays compatible with all parsers, and only a small number of
    bytes have to be moved around.

    The \c m_data member structure is also updated to reflect the
    changes made to the file.

    The function relies on \c m_data[data_idx] to be up to date
    regarding its size. If the size of \c m_data[data_idx] is zero
    then it is assumed that the element shall be overwritten with an
    EbmlVoid element, and \c m_data[data_idx] will be removed from the
    \c m_data structure.

    \param data_idx Index into the \c m_data structure pointing to the
     current element after which the gap is located.

    \return \c true if a new void element was created and \c false if
      there was no need to create one or if there was not enough
      space.
 */
bool
kax_analyzer_c::handle_void_elements(size_t data_idx) {
  // Is the element at the end of the file? If so truncate the file
  // and remove the element from the m_data structure if that was
  // requested. Then we're done.
  if (m_data.size() == (data_idx + 1)) {
    m_file->truncate(m_data[data_idx]->m_pos + m_data[data_idx]->m_size);
    adjust_segment_size();
    if (0 == m_data[data_idx]->m_size)
      m_data.erase(m_data.begin() + data_idx);
    return false;
  }

  // Are the following elements EbmlVoid elements?
  size_t end_idx = data_idx + 1;
  while ((m_data.size() > end_idx) && Is<EbmlVoid>(m_data[end_idx]->m_id))
    ++end_idx;

  if (end_idx > data_idx + 1)
    // Yes, there is at least one. Remove these elements from the list
    // in order to create a new EbmlVoid element covering their space
    // as well.
    m_data.erase(m_data.begin() + data_idx + 1, m_data.begin() + end_idx);

  // Calculate how much space we have to cover with a void
  // element. This is the difference between the next element's
  // position and the current element's end.
  int64_t void_pos = m_data[data_idx]->m_pos + m_data[data_idx]->m_size;
  int void_size    = m_data[data_idx + 1]->m_pos - void_pos;

  // If the difference is 0 then we have nothing to do.
  if (0 == void_size)
    return false;

  // See if we have enough space to fit an EbmlVoid element in. An
  // EbmlVoid element needs at least two bytes (one for the ID, one
  // for the size).
  if (1 == void_size) {
    // No. The most compatible way to deal with this situation is to
    // move the element ID of the following element one byte to the
    // front and extend the following element's size field by one
    // byte.

    ebml_element_cptr e = read_element(m_data[data_idx + 1]);

    if (!e)
      return false;

    // However, this might not work if the element's size was already
    // eight bytes long.
    if (8 == e->GetSizeLength()) {
      // In this case try doing the same with the previous
      // element. The whole element has be moved one byte to the back.
      e = read_element(m_data[data_idx]);
      if (!e)
        return false;

      std::shared_ptr<EbmlElement> af_e(e);

      // Again the test for maximum size length.
      if (8 == e->GetSizeLength())
        return false;

      // Copy the content one byte to the back.
      unsigned int id_length = EBML_ID_LENGTH(static_cast<const EbmlId &>(*e));
      uint64_t content_pos   = m_data[data_idx]->m_pos + id_length + e->GetSizeLength();
      uint64_t content_size  = m_data[data_idx + 1]->m_pos - content_pos - 1;
      memory_cptr buffer     = memory_c::alloc(content_size);

      m_file->setFilePointer(content_pos);
      if (m_file->read(buffer, content_size) != content_size)
        return false;

      m_file->setFilePointer(content_pos + 1);
      if (m_file->write(buffer) != content_size)
        return false;

      // Prepare the new codec size and write it.
      binary head[8];           // Class D + 64 bits coded size
      int coded_size = CodedSizeLength(content_size, e->GetSizeLength() + 1, true);
      CodedValueLength(content_size, coded_size, head);
      m_file->setFilePointer(m_data[data_idx]->m_pos + id_length);
      if (m_file->write(head, coded_size) != static_cast<unsigned int>(coded_size))
        return false;

      // Update internal structures.
      m_data[data_idx]->m_size += 1;

      return true;
    }

    binary head[4 + 8];         // Class D + 64 bits coded size
    unsigned int head_size = EBML_ID_LENGTH(static_cast<const EbmlId &>(*e));
    EbmlId(*e).Fill(head);

    int coded_size = CodedSizeLength(e->GetSize(), e->GetSizeLength() + 1, true);
    CodedValueLength(e->GetSize(), coded_size, &head[head_size]);
    head_size += coded_size;

    m_file->setFilePointer(m_data[data_idx + 1]->m_pos - 1);
    m_file->write(head, head_size);

    --m_data[data_idx + 1]->m_pos;
    ++m_data[data_idx + 1]->m_size;

    // Update meta seek indices for m_data[data_idx]'s new position.
    e = read_element(m_data[data_idx + 1]);

    remove_from_meta_seeks(EbmlId(*e));
    merge_void_elements();
    add_to_meta_seek(e.get());
    merge_void_elements();

    return false;
  }

  m_file->setFilePointer(void_pos);

  // Yes. Write a new EbmlVoid element and update the internal records.

  // Calculating the void element's content size. This is not straight
  // forward because there are special values for the total size for
  // which the header size must be forced to be one byte more than would
  // be strictly necessary in order to occupy the space. Here's an
  // example:

  // A two-byte header field (one for the ID, one for the content
  // size) can have a content size of at most 127 bytes, meaning a
  // total size of 129 is easy to achieve: set the content size to 127
  // bytes, libEBML will calculate the required length of the content
  // size field to be 1 and the resulting element's total size will be
  // the desired 129 bytes.

  // A content size of 128 would mean that the content size field must be
  // at least two bytes long. Taking the element's ID into account this
  // would mean a total element size of 128 + 2 + 1 = 131 bytes. So
  // getting libEBML to produce an element of a total size of 131 is
  // easy, too: set the content size to 128 bytes, and libEBML will do
  // the rest.

  // The problematic total size is a 130 bytes. There simply is no
  // content size for which adding the minimal length of the content
  // size field and 1 for the ID would result in a total size of 130
  // bytes.

  // The solution is writing the length field with more bytes than
  // necessary. In the case of a total size of 130 bytes we could use
  // the maximum one-byte content size = 127 bytes, add one byte for the
  // ID and force the content size field's length to be two instead of
  // one byte (0x407f instead of 0xff).

  // Similar corner cases exist for the transition between content
  // size field being two/three bytes, three/four bytes long etc. In
  // order to keep the code simple we always use an eight-bytes long
  // content size field if the total size is at least nine bytes and a
  // one-byte long content size field otherwise.

  EbmlVoid evoid;
  if (void_size < 9)
    evoid.SetSize(void_size - 2);

  else {
    evoid.SetSize(void_size - 9);
    evoid.SetSizeLength(8);
  }

  evoid.Render(*m_file);

  m_data.insert(m_data.begin() + data_idx + 1, kax_analyzer_data_c::create(EBML_ID(EbmlVoid), void_pos, void_size));

  // Now check if we should overwrite the current element with the
  // EbmlVoid element. That is the case if the current element's size
  // is 0. In that case simply remove the element from the m_data
  // vector.
  if (0 == m_data[data_idx]->m_size)
    m_data.erase(m_data.begin() + data_idx);

  return true;
}