001// Copyright (c) FIRST and other WPILib contributors. 002// Open Source Software; you can modify and/or share it under the terms of 003// the WPILib BSD license file in the root directory of this project. 004 005package edu.wpi.first.util.struct; 006 007import java.util.ArrayList; 008import java.util.HashMap; 009import java.util.List; 010import java.util.Map; 011import java.util.Stack; 012 013/** Raw struct dynamic struct descriptor. */ 014public class StructDescriptor { 015 StructDescriptor(String name) { 016 m_name = name; 017 } 018 019 /** 020 * Gets the struct name. 021 * 022 * @return name 023 */ 024 public String getName() { 025 return m_name; 026 } 027 028 /** 029 * Gets the struct schema. 030 * 031 * @return schema 032 */ 033 public String getSchema() { 034 return m_schema; 035 } 036 037 /** 038 * Returns whether the struct is valid (e.g. the struct is fully defined and field offsets 039 * computed). 040 * 041 * @return true if valid 042 */ 043 public boolean isValid() { 044 return m_valid; 045 } 046 047 /** 048 * Returns the struct size, in bytes. Not valid unless IsValid() is true. 049 * 050 * @return size in bytes 051 * @throws IllegalStateException if descriptor is invalid 052 */ 053 public int getSize() { 054 if (!m_valid) { 055 throw new IllegalStateException("descriptor is invalid"); 056 } 057 return m_size; 058 } 059 060 /** 061 * Gets a field descriptor by name. Note the field cannot be accessed until the struct is valid. 062 * 063 * @param name field name 064 * @return field descriptor, or nullptr if not found 065 */ 066 public StructFieldDescriptor findFieldByName(String name) { 067 return m_fieldsByName.get(name); 068 } 069 070 /** 071 * Gets all field descriptors. Note fields cannot be accessed until the struct is valid. 072 * 073 * @return field descriptors 074 */ 075 public List<StructFieldDescriptor> getFields() { 076 return m_fields; 077 } 078 079 boolean checkCircular(Stack<StructDescriptor> stack) { 080 stack.push(this); 081 for (StructDescriptor ref : m_references) { 082 if (stack.contains(ref)) { 083 return false; 084 } 085 if (!ref.checkCircular(stack)) { 086 return false; 087 } 088 } 089 stack.pop(); 090 return true; 091 } 092 093 void calculateOffsets(Stack<StructDescriptor> stack) { 094 int offset = 0; 095 int shift = 0; 096 int prevBitfieldSize = 0; 097 for (StructFieldDescriptor field : m_fields) { 098 if (!field.isBitField()) { 099 shift = 0; // reset shift on non-bitfield element 100 offset += prevBitfieldSize; // finish bitfield if active 101 prevBitfieldSize = 0; // previous is now not bitfield 102 field.m_offset = offset; 103 StructDescriptor struct = field.getStruct(); 104 if (struct != null) { 105 if (!struct.isValid()) { 106 m_valid = false; 107 return; 108 } 109 field.m_size = struct.m_size; 110 } 111 offset += field.m_size * field.m_arraySize; 112 } else { 113 int bitWidth = field.getBitWidth(); 114 if (field.getType() == StructFieldType.kBool 115 && prevBitfieldSize != 0 116 && (shift + 1) <= (prevBitfieldSize * 8)) { 117 // bool takes on size of preceding bitfield type (if it fits) 118 field.m_size = prevBitfieldSize; 119 } else if (field.m_size != prevBitfieldSize || (shift + bitWidth) > (field.m_size * 8)) { 120 shift = 0; 121 offset += prevBitfieldSize; 122 } 123 prevBitfieldSize = field.m_size; 124 field.m_offset = offset; 125 field.m_bitShift = shift; 126 shift += bitWidth; 127 } 128 } 129 130 // update struct size 131 m_size = offset + prevBitfieldSize; 132 m_valid = true; 133 134 // now that we're valid, referring types may be too 135 stack.push(this); 136 for (StructDescriptor ref : m_references) { 137 if (stack.contains(ref)) { 138 throw new IllegalStateException( 139 "internal error (inconsistent data): circular struct reference between " 140 + m_name 141 + " and " 142 + ref.m_name); 143 } 144 ref.calculateOffsets(stack); 145 } 146 stack.pop(); 147 } 148 149 private final String m_name; 150 String m_schema; 151 final List<StructDescriptor> m_references = new ArrayList<>(); 152 final List<StructFieldDescriptor> m_fields = new ArrayList<>(); 153 final Map<String, StructFieldDescriptor> m_fieldsByName = new HashMap<>(); 154 int m_size; 155 boolean m_valid; 156}