/// <summary> /// Identity constructor -- converts an array of fields to a padded struct data, /// with no padding or alignment sorting being performed. /// </summary> /// <param name="fields">The fields to convert</param> public PaddedStructData( McgField[] fields) { AlignmentEntries = new AlignmentEntry[fields.Length]; int i = 0; foreach (McgField field in fields) { AlignmentEntry entry = new AlignmentEntry(field, false, false); entry.Offset = PaddedSize; PaddedSize += field.Type.UnpaddedSize; AlignmentEntries[i++] = entry; } }
/// <summary> /// SortStructForAlignment - returns an array of AlignmentEntry's which describe /// the correct, padded/aligned layout for this struct. /// </summary> /// <returns> /// PaddedStructData - this contains an array of AlignmentEntry's which dictate the fields /// in the sorted struct and the resultant struct size. /// </returns> /// <param name="fields"> The fields to sort. </param> /// <param name="doAnimations"> Whether or not to add extra handles for animations. </param> /// <param name="padFullStructure"> Whether or not to pad the entire structure to a multiple of 8 bytes. </param> internal static PaddedStructData SortStructForAlignment( McgField[] fields, bool doAnimations, bool padFullStructure) { Debug.Assert(DuceHandle.Size == 4 || DuceHandle.Size == 8, "We assume UCE handles are 4 or 8 bytes."); bool alignHandles = DuceHandle.Size > 4; // // The algorithm used to align the fields is to sort the fields into three buckets: // // 1) QWord aligned fields whose length happens to be evenly divisible by 8. // 2) QWord aligned fields whose length is evenly divisible by 4, but not 8. // 3) DWords // // Fields in bucket 1 can be immediately emited because the following offset will // always be QWord aligned. Example: // // +----------+ // | 8 | // +----------+ <- Offset = 8 (QWord aligned) // | | // | 24 | // | | // +----------+ <- Offset = 32 (QWord aligned) // // At the end of the sorting we will then use fields in bucket 3 as padding for fields // in bucket 2. List <AlignmentEntry> sorted = new List <AlignmentEntry>(); List <AlignmentEntry> needsDwordPadding = new List <AlignmentEntry>(); List <AlignmentEntry> dwords = new List <AlignmentEntry>(); foreach (McgField field in fields) { if (field.Type.ShouldBeQuadWordAligned) { AlignmentEntry qwordEntry = new AlignmentEntry(field, /* isAnimation = */ false, /* isPad = */ false); int paddingNeeded = field.Type.UnpaddedSize % 8; if (paddingNeeded == 0) { sorted.Add(qwordEntry); } else if (paddingNeeded == 4) { needsDwordPadding.Add(qwordEntry); } else { Debug.Fail("We currently only support QWord/DWords aligment."); } } else { if (field.Type.UnpaddedSize == 4) { dwords.Add(new AlignmentEntry(field, /* isAnimation = */ false, /* isPad = */ false)); } else { Debug.Fail("We currently do not support fields smaller than 4 bytes."); } } if (doAnimations && field.IsAnimated) { AlignmentEntry animationEntry = new AlignmentEntry(field, /* isAnimation = */ true, /* isPadding = */ false); if (alignHandles) { sorted.Add(animationEntry); } else { dwords.Add(animationEntry); } } } // // Use our available DWord sized fields to pad any QWord aligned fields which // have lengths not evenly divisible 8. Example: // // +----------+ // | | // | 12 | // | | // +----------+ <- Offset = 12 (DWord aligned) // | 4 | // +----------+ <- Offset = 16 (QWord aligned) // // If we have no DWords available we create padding. // foreach (AlignmentEntry qwordEntry in needsDwordPadding) { sorted.Add(qwordEntry); if (dwords.Count > 0) { sorted.Add(dwords[0]); dwords.RemoveAt(0); } else { sorted.Add(new AlignmentEntry(/* field = */ null, /* isAnimation = */ false, /* isPad = */ true)); } } // // Emit any remaining DWords // foreach (AlignmentEntry dwordEntry in dwords) { sorted.Add(dwordEntry); } // // Loop through the list of sorted entries and compute the size of the struct. // int structSize = 0; foreach (AlignmentEntry entry in sorted) { structSize += entry.Size; } // // Pad the struct itself if necessary. // if (padFullStructure) { // Finally, the struct itself must be aligned if ((structSize % 8) != 0) { // We're only handling DWORD -> QWORD padding at this point Debug.Assert((structSize % 4) == 0); sorted.Add(new AlignmentEntry(/* field = */ null, /* isAnimation = */ false, /* isPad = */ true)); structSize += 4; } } AlignmentEntry[] asArray = (AlignmentEntry[])sorted.ToArray(); int offset = 0; // Now walk the list and add calculated offsets to each entry foreach (AlignmentEntry entry in asArray) { entry.Offset = offset; offset += entry.Size; } return(new PaddedStructData(asArray, structSize)); }