/** Encode the ACL to the given buffer */
  private static void encode(List<AclEntry> acl, long address) {
    long offset = address;
    for (AclEntry ace : acl) {
      int flags = 0;

      // map UserPrincipal to uid and flags
      UserPrincipal who = ace.principal();
      if (!(who instanceof UnixUserPrincipals.User)) throw new ProviderMismatchException();
      UnixUserPrincipals.User user = (UnixUserPrincipals.User) who;
      int uid;
      if (user.isSpecial()) {
        uid = -1;
        if (who == UnixUserPrincipals.SPECIAL_OWNER) flags |= ACE_OWNER;
        else if (who == UnixUserPrincipals.SPECIAL_GROUP)
          flags |= (ACE_GROUP | ACE_IDENTIFIER_GROUP);
        else if (who == UnixUserPrincipals.SPECIAL_EVERYONE) flags |= ACE_EVERYONE;
        else throw new AssertionError("Unable to map special identifier");
      } else {
        if (user instanceof UnixUserPrincipals.Group) {
          uid = user.gid();
          flags |= ACE_IDENTIFIER_GROUP;
        } else {
          uid = user.uid();
        }
      }

      // map ACE type
      int type;
      switch (ace.type()) {
        case ALLOW:
          type = ACE_ACCESS_ALLOWED_ACE_TYPE;
          break;
        case DENY:
          type = ACE_ACCESS_DENIED_ACE_TYPE;
          break;
        case AUDIT:
          type = ACE_SYSTEM_AUDIT_ACE_TYPE;
          break;
        case ALARM:
          type = ACE_SYSTEM_ALARM_ACE_TYPE;
          break;
        default:
          throw new AssertionError("Unable to map ACE type");
      }

      // map permissions
      Set<AclEntryPermission> aceMask = ace.permissions();
      int mask = 0;
      if (aceMask.contains(AclEntryPermission.READ_DATA)) mask |= ACE_READ_DATA;
      if (aceMask.contains(AclEntryPermission.WRITE_DATA)) mask |= ACE_WRITE_DATA;
      if (aceMask.contains(AclEntryPermission.APPEND_DATA)) mask |= ACE_APPEND_DATA;
      if (aceMask.contains(AclEntryPermission.READ_NAMED_ATTRS)) mask |= ACE_READ_NAMED_ATTRS;
      if (aceMask.contains(AclEntryPermission.WRITE_NAMED_ATTRS)) mask |= ACE_WRITE_NAMED_ATTRS;
      if (aceMask.contains(AclEntryPermission.EXECUTE)) mask |= ACE_EXECUTE;
      if (aceMask.contains(AclEntryPermission.DELETE_CHILD)) mask |= ACE_DELETE_CHILD;
      if (aceMask.contains(AclEntryPermission.READ_ATTRIBUTES)) mask |= ACE_READ_ATTRIBUTES;
      if (aceMask.contains(AclEntryPermission.WRITE_ATTRIBUTES)) mask |= ACE_WRITE_ATTRIBUTES;
      if (aceMask.contains(AclEntryPermission.DELETE)) mask |= ACE_DELETE;
      if (aceMask.contains(AclEntryPermission.READ_ACL)) mask |= ACE_READ_ACL;
      if (aceMask.contains(AclEntryPermission.WRITE_ACL)) mask |= ACE_WRITE_ACL;
      if (aceMask.contains(AclEntryPermission.WRITE_OWNER)) mask |= ACE_WRITE_OWNER;
      if (aceMask.contains(AclEntryPermission.SYNCHRONIZE)) mask |= ACE_SYNCHRONIZE;

      // FIXME - it would be desirable to know here if the file is a
      // directory or not. Solaris returns EINVAL if an ACE has a directory
      // -only flag and the file is not a directory.
      Set<AclEntryFlag> aceFlags = ace.flags();
      if (aceFlags.contains(AclEntryFlag.FILE_INHERIT)) flags |= ACE_FILE_INHERIT_ACE;
      if (aceFlags.contains(AclEntryFlag.DIRECTORY_INHERIT)) flags |= ACE_DIRECTORY_INHERIT_ACE;
      if (aceFlags.contains(AclEntryFlag.NO_PROPAGATE_INHERIT))
        flags |= ACE_NO_PROPAGATE_INHERIT_ACE;
      if (aceFlags.contains(AclEntryFlag.INHERIT_ONLY)) flags |= ACE_INHERIT_ONLY_ACE;

      unsafe.putInt(offset + OFFSETOF_UID, uid);
      unsafe.putInt(offset + OFFSETOF_MASK, mask);
      unsafe.putShort(offset + OFFSETOF_FLAGS, (short) flags);
      unsafe.putShort(offset + OFFSETOF_TYPE, (short) type);

      offset += SIZEOF_ACE_T;
    }
  }
  /** Decode the buffer, returning an ACL */
  private static List<AclEntry> decode(long address, int n) {
    ArrayList<AclEntry> acl = new ArrayList<>(n);
    for (int i = 0; i < n; i++) {
      long offset = address + i * SIZEOF_ACE_T;

      int uid = unsafe.getInt(offset + OFFSETOF_UID);
      int mask = unsafe.getInt(offset + OFFSETOF_MASK);
      int flags = (int) unsafe.getShort(offset + OFFSETOF_FLAGS);
      int type = (int) unsafe.getShort(offset + OFFSETOF_TYPE);

      // map uid and flags to UserPrincipal
      UnixUserPrincipals.User who = null;
      if ((flags & ACE_OWNER) > 0) {
        who = UnixUserPrincipals.SPECIAL_OWNER;
      } else if ((flags & ACE_GROUP) > 0) {
        who = UnixUserPrincipals.SPECIAL_GROUP;
      } else if ((flags & ACE_EVERYONE) > 0) {
        who = UnixUserPrincipals.SPECIAL_EVERYONE;
      } else if ((flags & ACE_IDENTIFIER_GROUP) > 0) {
        who = UnixUserPrincipals.fromGid(uid);
      } else {
        who = UnixUserPrincipals.fromUid(uid);
      }

      AclEntryType aceType = null;
      switch (type) {
        case ACE_ACCESS_ALLOWED_ACE_TYPE:
          aceType = AclEntryType.ALLOW;
          break;
        case ACE_ACCESS_DENIED_ACE_TYPE:
          aceType = AclEntryType.DENY;
          break;
        case ACE_SYSTEM_AUDIT_ACE_TYPE:
          aceType = AclEntryType.AUDIT;
          break;
        case ACE_SYSTEM_ALARM_ACE_TYPE:
          aceType = AclEntryType.ALARM;
          break;
        default:
          assert false;
      }

      Set<AclEntryPermission> aceMask = EnumSet.noneOf(AclEntryPermission.class);
      if ((mask & ACE_READ_DATA) > 0) aceMask.add(AclEntryPermission.READ_DATA);
      if ((mask & ACE_WRITE_DATA) > 0) aceMask.add(AclEntryPermission.WRITE_DATA);
      if ((mask & ACE_APPEND_DATA) > 0) aceMask.add(AclEntryPermission.APPEND_DATA);
      if ((mask & ACE_READ_NAMED_ATTRS) > 0) aceMask.add(AclEntryPermission.READ_NAMED_ATTRS);
      if ((mask & ACE_WRITE_NAMED_ATTRS) > 0) aceMask.add(AclEntryPermission.WRITE_NAMED_ATTRS);
      if ((mask & ACE_EXECUTE) > 0) aceMask.add(AclEntryPermission.EXECUTE);
      if ((mask & ACE_DELETE_CHILD) > 0) aceMask.add(AclEntryPermission.DELETE_CHILD);
      if ((mask & ACE_READ_ATTRIBUTES) > 0) aceMask.add(AclEntryPermission.READ_ATTRIBUTES);
      if ((mask & ACE_WRITE_ATTRIBUTES) > 0) aceMask.add(AclEntryPermission.WRITE_ATTRIBUTES);
      if ((mask & ACE_DELETE) > 0) aceMask.add(AclEntryPermission.DELETE);
      if ((mask & ACE_READ_ACL) > 0) aceMask.add(AclEntryPermission.READ_ACL);
      if ((mask & ACE_WRITE_ACL) > 0) aceMask.add(AclEntryPermission.WRITE_ACL);
      if ((mask & ACE_WRITE_OWNER) > 0) aceMask.add(AclEntryPermission.WRITE_OWNER);
      if ((mask & ACE_SYNCHRONIZE) > 0) aceMask.add(AclEntryPermission.SYNCHRONIZE);

      Set<AclEntryFlag> aceFlags = EnumSet.noneOf(AclEntryFlag.class);
      if ((flags & ACE_FILE_INHERIT_ACE) > 0) aceFlags.add(AclEntryFlag.FILE_INHERIT);
      if ((flags & ACE_DIRECTORY_INHERIT_ACE) > 0) aceFlags.add(AclEntryFlag.DIRECTORY_INHERIT);
      if ((flags & ACE_NO_PROPAGATE_INHERIT_ACE) > 0)
        aceFlags.add(AclEntryFlag.NO_PROPAGATE_INHERIT);
      if ((flags & ACE_INHERIT_ONLY_ACE) > 0) aceFlags.add(AclEntryFlag.INHERIT_ONLY);

      // build the ACL entry and add it to the list
      AclEntry ace =
          AclEntry.newBuilder()
              .setType(aceType)
              .setPrincipal(who)
              .setPermissions(aceMask)
              .setFlags(aceFlags)
              .build();
      acl.add(ace);
    }

    return acl;
  }