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