PRBool nsSkeletonState::DecodeIndex(ogg_packet* aPacket)
{
  NS_ASSERTION(aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN,
               "Index must be at least minimum size");
  if (!mActive) {
    return PR_FALSE;
  }

  PRUint32 serialno = LEUint32(aPacket->packet + INDEX_SERIALNO_OFFSET);
  PRInt64 numKeyPoints = LEInt64(aPacket->packet + INDEX_NUM_KEYPOINTS_OFFSET);

  PRInt64 n = 0;
  PRInt64 endTime = 0, startTime = 0;
  const unsigned char* p = aPacket->packet;

  PRInt64 timeDenom = LEInt64(aPacket->packet + INDEX_TIME_DENOM_OFFSET);
  if (timeDenom == 0) {
    LOG(PR_LOG_DEBUG, ("Ogg Skeleton Index packet for stream %u has 0 "
                       "timestamp denominator.", serialno));
    return (mActive = PR_FALSE);
  }

  // Extract the start time.
  n = LEInt64(p + INDEX_FIRST_NUMER_OFFSET);
  PRInt64 t;
  if (!MulOverflow(n, 1000, t)) {
    return (mActive = PR_FALSE);
  } else {
    startTime = t / timeDenom;
  }

  // Extract the end time.
  n = LEInt64(p + INDEX_LAST_NUMER_OFFSET);
  if (!MulOverflow(n, 1000, t)) {
    return (mActive = PR_FALSE);
  } else {
    endTime = t / timeDenom;
  }

  // Check the numKeyPoints value read, ensure we're not going to run out of
  // memory while trying to decode the index packet.
  PRInt64 minPacketSize;
  if (!MulOverflow(numKeyPoints, MIN_KEY_POINT_SIZE, minPacketSize) ||
      !AddOverflow(INDEX_KEYPOINT_OFFSET, minPacketSize, minPacketSize))
  {
    return (mActive = PR_FALSE);
  }
  
  PRInt64 sizeofIndex = aPacket->bytes - INDEX_KEYPOINT_OFFSET;
  PRInt64 maxNumKeyPoints = sizeofIndex / MIN_KEY_POINT_SIZE;
  if (aPacket->bytes < minPacketSize ||
      numKeyPoints > maxNumKeyPoints || 
      numKeyPoints < 0)
  {
    // Packet size is less than the theoretical minimum size, or the packet is
    // claiming to store more keypoints than it's capable of storing. This means
    // that the numKeyPoints field is too large or small for the packet to
    // possibly contain as many packets as it claims to, so the numKeyPoints
    // field is possibly malicious. Don't try decoding this index, we may run
    // out of memory.
    LOG(PR_LOG_DEBUG, ("Possibly malicious number of key points reported "
                       "(%lld) in index packet for stream %u.",
                       numKeyPoints,
                       serialno));
    return (mActive = PR_FALSE);
  }

  nsAutoPtr<nsKeyFrameIndex> keyPoints(new nsKeyFrameIndex(startTime, endTime));
  
  p = aPacket->packet + INDEX_KEYPOINT_OFFSET;
  const unsigned char* limit = aPacket->packet + aPacket->bytes;
  PRInt64 numKeyPointsRead = 0;
  PRInt64 offset = 0;
  PRInt64 time = 0;
  while (p < limit &&
         numKeyPointsRead < numKeyPoints)
  {
    PRInt64 delta = 0;
    p = ReadVariableLengthInt(p, limit, delta);
    if (p == limit ||
        !AddOverflow(offset, delta, offset) ||
        offset > mLength ||
        offset < 0)
    {
      return (mActive = PR_FALSE);
    }
    p = ReadVariableLengthInt(p, limit, delta);
    if (!AddOverflow(time, delta, time) ||
        time > endTime ||
        time < startTime)
    {
      return (mActive = PR_FALSE);
    }
    PRInt64 timeMs = 0;
    if (!MulOverflow(time, 1000, timeMs))
      return mActive = PR_FALSE;
    timeMs /= timeDenom;
    keyPoints->Add(offset, timeMs);
    numKeyPointsRead++;
  }

  PRInt32 keyPointsRead = keyPoints->Length();
  if (keyPointsRead > 0) {
    mIndex.Put(serialno, keyPoints.forget());
  }

  LOG(PR_LOG_DEBUG, ("Loaded %d keypoints for Skeleton on stream %u",
                     keyPointsRead, serialno));
  return PR_TRUE;
}
bool SkeletonState::DecodeIndex(ogg_packet* aPacket)
{
  NS_ASSERTION(aPacket->bytes >= SKELETON_4_0_MIN_INDEX_LEN,
               "Index must be at least minimum size");
  if (!mActive) {
    return false;
  }

  uint32_t serialno = LittleEndian::readUint32(aPacket->packet + INDEX_SERIALNO_OFFSET);
  int64_t numKeyPoints = LittleEndian::readInt64(aPacket->packet + INDEX_NUM_KEYPOINTS_OFFSET);

  int64_t endTime = 0, startTime = 0;
  const unsigned char* p = aPacket->packet;

  int64_t timeDenom = LittleEndian::readInt64(aPacket->packet + INDEX_TIME_DENOM_OFFSET);
  if (timeDenom == 0) {
    LOG(PR_LOG_DEBUG, ("Ogg Skeleton Index packet for stream %u has 0 "
                       "timestamp denominator.", serialno));
    return (mActive = false);
  }

  // Extract the start time.
  CheckedInt64 t = CheckedInt64(LittleEndian::readInt64(p + INDEX_FIRST_NUMER_OFFSET)) * USECS_PER_S;
  if (!t.isValid()) {
    return (mActive = false);
  } else {
    startTime = t.value() / timeDenom;
  }

  // Extract the end time.
  t = LittleEndian::readInt64(p + INDEX_LAST_NUMER_OFFSET) * USECS_PER_S;
  if (!t.isValid()) {
    return (mActive = false);
  } else {
    endTime = t.value() / timeDenom;
  }

  // Check the numKeyPoints value read, ensure we're not going to run out of
  // memory while trying to decode the index packet.
  CheckedInt64 minPacketSize = (CheckedInt64(numKeyPoints) * MIN_KEY_POINT_SIZE) + INDEX_KEYPOINT_OFFSET;
  if (!minPacketSize.isValid())
  {
    return (mActive = false);
  }
  
  int64_t sizeofIndex = aPacket->bytes - INDEX_KEYPOINT_OFFSET;
  int64_t maxNumKeyPoints = sizeofIndex / MIN_KEY_POINT_SIZE;
  if (aPacket->bytes < minPacketSize.value() ||
      numKeyPoints > maxNumKeyPoints || 
      numKeyPoints < 0)
  {
    // Packet size is less than the theoretical minimum size, or the packet is
    // claiming to store more keypoints than it's capable of storing. This means
    // that the numKeyPoints field is too large or small for the packet to
    // possibly contain as many packets as it claims to, so the numKeyPoints
    // field is possibly malicious. Don't try decoding this index, we may run
    // out of memory.
    LOG(PR_LOG_DEBUG, ("Possibly malicious number of key points reported "
                       "(%lld) in index packet for stream %u.",
                       numKeyPoints,
                       serialno));
    return (mActive = false);
  }

  nsAutoPtr<nsKeyFrameIndex> keyPoints(new nsKeyFrameIndex(startTime, endTime));
  
  p = aPacket->packet + INDEX_KEYPOINT_OFFSET;
  const unsigned char* limit = aPacket->packet + aPacket->bytes;
  int64_t numKeyPointsRead = 0;
  CheckedInt64 offset = 0;
  CheckedInt64 time = 0;
  while (p < limit &&
         numKeyPointsRead < numKeyPoints)
  {
    int64_t delta = 0;
    p = ReadVariableLengthInt(p, limit, delta);
    offset += delta;
    if (p == limit ||
        !offset.isValid() ||
        offset.value() > mLength ||
        offset.value() < 0)
    {
      return (mActive = false);
    }
    p = ReadVariableLengthInt(p, limit, delta);
    time += delta;
    if (!time.isValid() ||
        time.value() > endTime ||
        time.value() < startTime)
    {
      return (mActive = false);
    }
    CheckedInt64 timeUsecs = time * USECS_PER_S;
    if (!timeUsecs.isValid())
      return mActive = false;
    timeUsecs /= timeDenom;
    keyPoints->Add(offset.value(), timeUsecs.value());
    numKeyPointsRead++;
  }

  int32_t keyPointsRead = keyPoints->Length();
  if (keyPointsRead > 0) {
    mIndex.Put(serialno, keyPoints.forget());
  }

  LOG(PR_LOG_DEBUG, ("Loaded %d keypoints for Skeleton on stream %u",
                     keyPointsRead, serialno));
  return true;
}