/**
   * Create a new binary rating packer.
   *
   * @param file The output file.
   * @throws IOException The output exception.
   */
  BinaryRatingPacker(File file, EnumSet<BinaryFormatFlag> flags) throws IOException {
    format = BinaryFormat.createWithFlags(PackHeaderFlag.fromFormatFlags(flags));
    outputFile = file;

    logger.debug("opening binary pack file {}", outputFile);
    output = new RandomAccessFile(file, "rw");
    channel = output.getChannel();

    userMap = new Long2ObjectOpenHashMap<IntList>();
    itemMap = new Long2ObjectOpenHashMap<IntList>();

    lastTimestamp = Long.MIN_VALUE;
    needsSorting = false;
    index = 0;

    // skip the header
    channel.position(BinaryHeader.HEADER_SIZE);

    ratingBuffer = ByteBuffer.allocateDirect(format.getRatingSize());
  }
  private void checkUpgrade(long uid, long iid) throws IOException {
    Set<PackHeaderFlag> toRemove = null;
    if (!format.userIdIsValid(uid)) {
      assert format.hasCompactUsers();
      toRemove = EnumSet.of(PackHeaderFlag.COMPACT_USERS);
    }
    if (!format.itemIdIsValid(iid)) {
      assert format.hasCompactItems();
      if (toRemove == null) {
        toRemove = EnumSet.of(PackHeaderFlag.COMPACT_ITEMS);
      } else {
        toRemove.add(PackHeaderFlag.COMPACT_ITEMS);
      }
    }

    if (toRemove != null) {
      Set<PackHeaderFlag> newFlags = EnumSet.copyOf(format.getFlags());
      newFlags.removeAll(toRemove);
      BinaryFormat newFormat = BinaryFormat.createWithFlags(newFlags);
      if (newFormat != format) {
        upgradeRatings(newFormat);
      }
    }
  }