public <E extends Exception> void forEachAttribute(Processor<E> processor) throws IOException, E {
    // RC: For dedicated records ID of dedicated record reported, instead of id of apt. parent 'directory' record -- simply
    // because this is there attribute value is found during file scan-through -- while at insert phase it would be _directory
    // record_ id.
    // But such a behaviour, surely, a surprise from a client PoV.
    // And surely, this could be fixed in the implementation, but with the cost: basically, we'll need to not report dedicated
    // records to the callback immediately, but keep track of them, and report them only after their 'parent' directory records
    // are met, hence 'true' recordId could be determined.
    // This is surely doable, but for now it seems an overkill to do that -- for the practical use cases of .forEachAttribute I
    // have in my mind now fileId and attributeId are important, but 'true' recordId is really not important. And the purpose of
    // this method is to provide fast _streaming-like_ access to storage.
    // Hence, I decided to delay more correct implementation until the need for it satisfies its cost.
    lock.readLock().lock();
    try {
      storage.forEach((recordId, recordCapacity, recordLength, payload) -> {
        if (!storage.isRecordActual(recordLength)) {
          return true;
        }

        AttributesRecord attributesRecord = new AttributesRecord(payload);
        if (attributesRecord.hasDirectory()) {
          int fileId = attributesRecord.fileId();
          for (AttributeEntry attributeEntry = attributesRecord.currentEntry();
               attributeEntry.isValid();
               attributeEntry.moveToNextEntry()) {
            int attributeId = attributeEntry.attributeId();
            if (attributeEntry.isValueInlined()) {
              byte[] valueBytes = attributeEntry.inlinedValueAsByteArray();
              processor.processAttribute(recordId, fileId, attributeId, valueBytes, /*inlined: */ true);
            }
          }
        }
        else if (attributesRecord.hasDedicatedAttribute()) {
          int fileId = attributesRecord.fileId();
          int attributeId = attributesRecord.dedicatedRecordAttributeId();
          byte[] valueBytes = attributesRecord.dedicatedValueAsByteArray();
          processor.processAttribute(recordId, fileId, attributeId, valueBytes, /*inlined: */ false);
        }
        return true;
      });
    }
    finally {
      lock.readLock().unlock();
    }
  }
public <E extends Exception> void forEachAttribute(Processor<E> processor)