public static <E extends Enum<E>> DWARFAttributeDef<E> read(BinaryReader reader,
			Function<Integer, E> mapper) throws IOException {

		int attributeId = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
		int formId = reader.readNextUnsignedVarIntExact(LEB128::unsigned);
		
		if (attributeId == DWARFAttribute.EOL && formId == DWARFForm.EOL) {
			// end of attributespec list
			return null;
		}

		DWARFForm form = DWARFForm.of(formId);
		if ( form == null ) {
			throw new IOException("Unknown DWARFForm %d (0x%x)".formatted(formId, formId));
		}

		E e = mapper.apply(attributeId);

		// NOTE: implicit value is a space saving hack built into DWARF.  It adds an extra
		// field in the attributespec that needs to be read now in the .debug_abbr.  This is 
		// different than DW_FORM_indirect, which is read from the DIE in .debug_info
		long implicitValue = form == DWARFForm.DW_FORM_implicit_const // read leb128 if present 
				? reader.readNext(LEB128::signed)
				: 0;

		return new DWARFAttributeDef<>(e, attributeId, form, implicitValue);
	}
Reads a {@link DWARFAttributeDef} instance from the {@link BinaryReader reader}.
<p>
Returns a null if its a end-of-list marker (which is only used by an attributespec list).

@param <E> attribute id enum type
@param reader {@link BinaryReader}
@param mapper func that converts an attribute id int into its enum
@return DWARFAttributeDef instance, or null if EOL marker was read from the stream 
@throws IOException if error reading
Ghidra/Features/Base/src/main/java/ghidra/app/util/bin/format/dwarf/attribs/DWARFAttributeDef.java