From: James McCoy <jamessan@debian.org>
Date: Thu, 16 Oct 2025 21:05:21 -0400
Subject: Revert "fix(lib): replace raw array accesses with `array_get`"

This reverts commit b890e8bea0550d158ccd72f826bdee700f5a6e30.
---
 lib/src/get_changed_ranges.c |  12 +-
 lib/src/parser.c             |  50 ++++----
 lib/src/query.c              | 283 +++++++++++++++++++++----------------------
 lib/src/stack.c              |  65 +++++-----
 lib/src/subtree.c            |  14 +--
 lib/src/tree_cursor.c        |  22 ++--
 lib/src/wasm_store.c         |  10 +-
 7 files changed, 227 insertions(+), 229 deletions(-)

diff --git a/lib/src/get_changed_ranges.c b/lib/src/get_changed_ranges.c
index 11084c3..b8130a1 100644
--- a/lib/src/get_changed_ranges.c
+++ b/lib/src/get_changed_ranges.c
@@ -34,7 +34,7 @@ bool ts_range_array_intersects(
   uint32_t end_byte
 ) {
   for (unsigned i = start_index; i < self->size; i++) {
-    TSRange *range = array_get(self, i);
+    TSRange *range = &self->contents[i];
     if (range->end_byte > start_byte) {
       if (range->start_byte >= end_byte) break;
       return true;
@@ -159,7 +159,7 @@ static bool iterator_tree_is_visible(const Iterator *self) {
   TreeCursorEntry entry = *array_back(&self->cursor.stack);
   if (ts_subtree_visible(*entry.subtree)) return true;
   if (self->cursor.stack.size > 1) {
-    Subtree parent = *array_get(&self->cursor.stack, self->cursor.stack.size - 2)->subtree;
+    Subtree parent = *self->cursor.stack.contents[self->cursor.stack.size - 2].subtree;
     return ts_language_alias_at(
       self->language,
       parent.ptr->production_id,
@@ -183,10 +183,10 @@ static void iterator_get_visible_state(
   }
 
   for (; i + 1 > 0; i--) {
-    TreeCursorEntry entry = *array_get(&self->cursor.stack, i);
+    TreeCursorEntry entry = self->cursor.stack.contents[i];
 
     if (i > 0) {
-      const Subtree *parent = array_get(&self->cursor.stack, i - 1)->subtree;
+      const Subtree *parent = self->cursor.stack.contents[i - 1].subtree;
       *alias_symbol = ts_language_alias_at(
         self->language,
         parent->ptr->production_id,
@@ -497,9 +497,9 @@ unsigned ts_subtree_get_changed_ranges(
     // Keep track of the current position in the included range differences
     // array in order to avoid scanning the entire array on each iteration.
     while (included_range_difference_index < included_range_differences->size) {
-      const TSRange *range = array_get(included_range_differences,
+      const TSRange *range = &included_range_differences->contents[
         included_range_difference_index
-      );
+      ];
       if (range->end_byte <= position.bytes) {
         included_range_difference_index++;
       } else {
diff --git a/lib/src/parser.c b/lib/src/parser.c
index d0a2d2c..896ea4e 100644
--- a/lib/src/parser.c
+++ b/lib/src/parser.c
@@ -193,7 +193,7 @@ static bool ts_parser__breakdown_top_of_stack(
     did_break_down = true;
     pending = false;
     for (uint32_t i = 0; i < pop.size; i++) {
-      StackSlice slice = *array_get(&pop, i);
+      StackSlice slice = pop.contents[i];
       TSStateId state = ts_stack_state(self->stack, slice.version);
       Subtree parent = *array_front(&slice.subtrees);
 
@@ -212,7 +212,7 @@ static bool ts_parser__breakdown_top_of_stack(
       }
 
       for (uint32_t j = 1; j < slice.subtrees.size; j++) {
-        Subtree tree = *array_get(&slice.subtrees, j);
+        Subtree tree = slice.subtrees.contents[j];
         ts_stack_push(self->stack, slice.version, tree, false, state);
       }
 
@@ -951,7 +951,7 @@ static StackVersion ts_parser__reduce(
   uint32_t removed_version_count = 0;
   uint32_t halted_version_count = ts_stack_halted_version_count(self->stack);
   for (uint32_t i = 0; i < pop.size; i++) {
-    StackSlice slice = *array_get(&pop, i);
+    StackSlice slice = pop.contents[i];
     StackVersion slice_version = slice.version - removed_version_count;
 
     // This is where new versions are added to the parse stack. The versions
@@ -964,7 +964,7 @@ static StackVersion ts_parser__reduce(
       removed_version_count++;
       while (i + 1 < pop.size) {
         LOG("aborting reduce with too many versions")
-        StackSlice next_slice = *array_get(&pop, i + 1);
+        StackSlice next_slice = pop.contents[i + 1];
         if (next_slice.version != slice.version) break;
         ts_subtree_array_delete(&self->tree_pool, &next_slice.subtrees);
         i++;
@@ -987,7 +987,7 @@ static StackVersion ts_parser__reduce(
     // choose one of the arrays of trees to be the parent node's children, and
     // delete the rest of the tree arrays.
     while (i + 1 < pop.size) {
-      StackSlice next_slice = *array_get(&pop, i + 1);
+      StackSlice next_slice = pop.contents[i + 1];
       if (next_slice.version != slice.version) break;
       i++;
 
@@ -1029,7 +1029,7 @@ static StackVersion ts_parser__reduce(
     // were previously on top of the stack.
     ts_stack_push(self->stack, slice_version, ts_subtree_from_mut(parent), false, next_state);
     for (uint32_t j = 0; j < self->trailing_extras.size; j++) {
-      ts_stack_push(self->stack, slice_version, *array_get(&self->trailing_extras, j), false, next_state);
+      ts_stack_push(self->stack, slice_version, self->trailing_extras.contents[j], false, next_state);
     }
 
     for (StackVersion j = 0; j < slice_version; j++) {
@@ -1057,11 +1057,11 @@ static void ts_parser__accept(
 
   StackSliceArray pop = ts_stack_pop_all(self->stack, version);
   for (uint32_t i = 0; i < pop.size; i++) {
-    SubtreeArray trees = array_get(&pop, i)->subtrees;
+    SubtreeArray trees = pop.contents[i].subtrees;
 
     Subtree root = NULL_SUBTREE;
     for (uint32_t j = trees.size - 1; j + 1 > 0; j--) {
-      Subtree tree = *array_get(&trees, j);
+      Subtree tree = trees.contents[j];
       if (!ts_subtree_extra(tree)) {
         ts_assert(!tree.data.is_inline);
         uint32_t child_count = ts_subtree_child_count(tree);
@@ -1096,7 +1096,7 @@ static void ts_parser__accept(
     }
   }
 
-  ts_stack_remove_version(self->stack, array_get(&pop, 0)->version);
+  ts_stack_remove_version(self->stack, pop.contents[0].version);
   ts_stack_halt(self->stack, version);
 }
 
@@ -1162,7 +1162,7 @@ static bool ts_parser__do_all_potential_reductions(
 
     StackVersion reduction_version = STACK_VERSION_NONE;
     for (uint32_t j = 0; j < self->reduce_actions.size; j++) {
-      ReduceAction action = *array_get(&self->reduce_actions, j);
+      ReduceAction action = self->reduce_actions.contents[j];
 
       reduction_version = ts_parser__reduce(
         self, version, action.symbol, action.count,
@@ -1200,7 +1200,7 @@ static bool ts_parser__recover_to_state(
   StackVersion previous_version = STACK_VERSION_NONE;
 
   for (unsigned i = 0; i < pop.size; i++) {
-    StackSlice slice = *array_get(&pop, i);
+    StackSlice slice = pop.contents[i];
 
     if (slice.version == previous_version) {
       ts_subtree_array_delete(&self->tree_pool, &slice.subtrees);
@@ -1218,12 +1218,12 @@ static bool ts_parser__recover_to_state(
     SubtreeArray error_trees = ts_stack_pop_error(self->stack, slice.version);
     if (error_trees.size > 0) {
       ts_assert(error_trees.size == 1);
-      Subtree error_tree = *array_get(&error_trees, 0);
+      Subtree error_tree = error_trees.contents[0];
       uint32_t error_child_count = ts_subtree_child_count(error_tree);
       if (error_child_count > 0) {
         array_splice(&slice.subtrees, 0, 0, error_child_count, ts_subtree_children(error_tree));
         for (unsigned j = 0; j < error_child_count; j++) {
-          ts_subtree_retain(*array_get(&slice.subtrees, j));
+          ts_subtree_retain(slice.subtrees.contents[j]);
         }
       }
       ts_subtree_array_delete(&self->tree_pool, &error_trees);
@@ -1239,7 +1239,7 @@ static bool ts_parser__recover_to_state(
     }
 
     for (unsigned j = 0; j < self->trailing_extras.size; j++) {
-      Subtree tree = *array_get(&self->trailing_extras, j);
+      Subtree tree = self->trailing_extras.contents[j];
       ts_stack_push(self->stack, slice.version, tree, false, goal_state);
     }
 
@@ -1275,7 +1275,7 @@ static void ts_parser__recover(
   // if the current lookahead token would be valid in that state.
   if (summary && !ts_subtree_is_error(lookahead)) {
     for (unsigned i = 0; i < summary->size; i++) {
-      StackSummaryEntry entry = *array_get(summary, i);
+      StackSummaryEntry entry = summary->contents[i];
 
       if (entry.state == ERROR_STATE) continue;
       if (entry.position.bytes == position.bytes) continue;
@@ -1402,18 +1402,18 @@ static void ts_parser__recover(
     // arbitrarily and discard the rest.
     if (pop.size > 1) {
       for (unsigned i = 1; i < pop.size; i++) {
-        ts_subtree_array_delete(&self->tree_pool, &array_get(&pop, i)->subtrees);
+        ts_subtree_array_delete(&self->tree_pool, &pop.contents[i].subtrees);
       }
-      while (ts_stack_version_count(self->stack) > array_get(&pop, 0)->version + 1) {
-        ts_stack_remove_version(self->stack, array_get(&pop, 0)->version + 1);
+      while (ts_stack_version_count(self->stack) > pop.contents[0].version + 1) {
+        ts_stack_remove_version(self->stack, pop.contents[0].version + 1);
       }
     }
 
-    ts_stack_renumber_version(self->stack, array_get(&pop, 0)->version, version);
-    array_push(&array_get(&pop, 0)->subtrees, ts_subtree_from_mut(error_repeat));
+    ts_stack_renumber_version(self->stack, pop.contents[0].version, version);
+    array_push(&pop.contents[0].subtrees, ts_subtree_from_mut(error_repeat));
     error_repeat = ts_subtree_new_node(
       ts_builtin_sym_error_repeat,
-      &array_get(&pop, 0)->subtrees,
+      &pop.contents[0].subtrees,
       0,
       self->language
     );
@@ -1889,9 +1889,9 @@ static bool ts_parser__balance_subtree(TSParser *self) {
       return false;
     }
 
-    MutableSubtree tree = *array_get(&self->tree_pool.tree_stack, 
+    MutableSubtree tree = self->tree_pool.tree_stack.contents[
       self->tree_pool.tree_stack.size - 1
-    );
+    ];
 
     if (tree.ptr->repeat_depth > 0) {
       Subtree child1 = ts_subtree_children(tree)[0];
@@ -2140,7 +2140,7 @@ TSTree *ts_parser_parse(
       LOG("parse_after_edit");
       LOG_TREE(self->old_tree);
       for (unsigned i = 0; i < self->included_range_differences.size; i++) {
-        TSRange *range = array_get(&self->included_range_differences, i);
+        TSRange *range = &self->included_range_differences.contents[i];
         LOG("different_included_range %u - %u", range->start_byte, range->end_byte);
       }
     } else {
@@ -2197,7 +2197,7 @@ TSTree *ts_parser_parse(
     }
 
     while (self->included_range_difference_index < self->included_range_differences.size) {
-      TSRange *range = array_get(&self->included_range_differences, self->included_range_difference_index);
+      TSRange *range = &self->included_range_differences.contents[self->included_range_difference_index];
       if (range->end_byte <= position) {
         self->included_range_difference_index++;
       } else {
diff --git a/lib/src/query.c b/lib/src/query.c
index 6c51488..c54f7d5 100644
--- a/lib/src/query.c
+++ b/lib/src/query.c
@@ -437,26 +437,26 @@ static CaptureListPool capture_list_pool_new(void) {
 static void capture_list_pool_reset(CaptureListPool *self) {
   for (uint16_t i = 0; i < (uint16_t)self->list.size; i++) {
     // This invalid size means that the list is not in use.
-    array_get(&self->list, i)->size = UINT32_MAX;
+    self->list.contents[i].size = UINT32_MAX;
   }
   self->free_capture_list_count = self->list.size;
 }
 
 static void capture_list_pool_delete(CaptureListPool *self) {
   for (uint16_t i = 0; i < (uint16_t)self->list.size; i++) {
-    array_delete(array_get(&self->list, i));
+    array_delete(&self->list.contents[i]);
   }
   array_delete(&self->list);
 }
 
 static const CaptureList *capture_list_pool_get(const CaptureListPool *self, uint16_t id) {
   if (id >= self->list.size) return &self->empty_list;
-  return array_get(&self->list, id);
+  return &self->list.contents[id];
 }
 
 static CaptureList *capture_list_pool_get_mut(CaptureListPool *self, uint16_t id) {
   ts_assert(id < self->list.size);
-  return array_get(&self->list, id);
+  return &self->list.contents[id];
 }
 
 static bool capture_list_pool_is_empty(const CaptureListPool *self) {
@@ -469,8 +469,8 @@ static uint16_t capture_list_pool_acquire(CaptureListPool *self) {
   // First see if any already allocated capture list is currently unused.
   if (self->free_capture_list_count > 0) {
     for (uint16_t i = 0; i < (uint16_t)self->list.size; i++) {
-      if (array_get(&self->list, i)->size == UINT32_MAX) {
-        array_clear(array_get(&self->list, i));
+      if (self->list.contents[i].size == UINT32_MAX) {
+        array_clear(&self->list.contents[i]);
         self->free_capture_list_count--;
         return i;
       }
@@ -491,7 +491,7 @@ static uint16_t capture_list_pool_acquire(CaptureListPool *self) {
 
 static void capture_list_pool_release(CaptureListPool *self, uint16_t id) {
   if (id >= self->list.size) return;
-  array_get(&self->list, id)->size = UINT32_MAX;
+  self->list.contents[id].size = UINT32_MAX;
   self->free_capture_list_count++;
 }
 
@@ -774,10 +774,10 @@ static int symbol_table_id_for_name(
   uint32_t length
 ) {
   for (unsigned i = 0; i < self->slices.size; i++) {
-    Slice slice = *array_get(&self->slices, i);
+    Slice slice = self->slices.contents[i];
     if (
       slice.length == length &&
-      !strncmp(array_get(&self->characters, slice.offset), name, length)
+      !strncmp(&self->characters.contents[slice.offset], name, length)
     ) return i;
   }
   return -1;
@@ -788,9 +788,9 @@ static const char *symbol_table_name_for_id(
   uint16_t id,
   uint32_t *length
 ) {
-  Slice slice = *(array_get(&self->slices,id));
+  Slice slice = self->slices.contents[id];
   *length = slice.length;
-  return array_get(&self->characters, slice.offset);
+  return &self->characters.contents[slice.offset];
 }
 
 static uint16_t symbol_table_insert_name(
@@ -805,8 +805,8 @@ static uint16_t symbol_table_insert_name(
     .length = length,
   };
   array_grow_by(&self->characters, length + 1);
-  memcpy(array_get(&self->characters, slice.offset), name, length);
-  *array_get(&self->characters, self->characters.size - 1) = 0;
+  memcpy(&self->characters.contents[slice.offset], name, length);
+  self->characters.contents[self->characters.size - 1] = 0;
   array_push(&self->slices, slice);
   return self->slices.size - 1;
 }
@@ -1109,23 +1109,23 @@ static inline bool ts_query__pattern_map_search(
   while (size > 1) {
     uint32_t half_size = size / 2;
     uint32_t mid_index = base_index + half_size;
-    TSSymbol mid_symbol = array_get(&self->steps,
-      array_get(&self->pattern_map, mid_index)->step_index
-    )->symbol;
+    TSSymbol mid_symbol = self->steps.contents[
+      self->pattern_map.contents[mid_index].step_index
+    ].symbol;
     if (needle > mid_symbol) base_index = mid_index;
     size -= half_size;
   }
 
-  TSSymbol symbol = array_get(&self->steps,
-    array_get(&self->pattern_map, base_index)->step_index
-  )->symbol;
+  TSSymbol symbol = self->steps.contents[
+    self->pattern_map.contents[base_index].step_index
+  ].symbol;
 
   if (needle > symbol) {
     base_index++;
     if (base_index < self->pattern_map.size) {
-      symbol = array_get(&self->steps,
-        array_get(&self->pattern_map, base_index)->step_index
-      )->symbol;
+      symbol = self->steps.contents[
+        self->pattern_map.contents[base_index].step_index
+      ].symbol;
     }
   }
 
@@ -1148,9 +1148,9 @@ static inline void ts_query__pattern_map_insert(
   // initiated first, which allows the ordering of the states array
   // to be maintained more efficiently.
   while (index < self->pattern_map.size) {
-    PatternEntry *entry = array_get(&self->pattern_map, index);
+    PatternEntry *entry = &self->pattern_map.contents[index];
     if (
-      array_get(&self->steps, entry->step_index)->symbol == symbol &&
+      self->steps.contents[entry->step_index].symbol == symbol &&
       entry->pattern_index < new_entry.pattern_index
     ) {
       index++;
@@ -1183,11 +1183,11 @@ static void ts_query__perform_analysis(
     #ifdef DEBUG_ANALYZE_QUERY
       printf("Iteration: %u. Final step indices:", iteration);
       for (unsigned j = 0; j < analysis->final_step_indices.size; j++) {
-        printf(" %4u", *array_get(&analysis->final_step_indices, j));
+        printf(" %4u", analysis->final_step_indices.contents[j]);
       }
       printf("\n");
       for (unsigned j = 0; j < analysis->states.size; j++) {
-        AnalysisState *state = *array_get(&analysis->states, j);
+        AnalysisState *state = analysis->states.contents[j];
         printf("  %3u: step: %u, stack: [", j, state->step_index);
         for (unsigned k = 0; k < state->depth; k++) {
           printf(
@@ -1230,7 +1230,7 @@ static void ts_query__perform_analysis(
 
     analysis_state_set__clear(&analysis->next_states, &analysis->state_pool);
     for (unsigned j = 0; j < analysis->states.size; j++) {
-      AnalysisState * const state = *array_get(&analysis->states, j);
+      AnalysisState * const state = analysis->states.contents[j];
 
       // For efficiency, it's important to avoid processing the same analysis state more
       // than once. To achieve this, keep the states in order of ascending position within
@@ -1253,7 +1253,7 @@ static void ts_query__perform_analysis(
             analysis_state_set__push(
               &analysis->next_states,
               &analysis->state_pool,
-              *array_get(&analysis->states, j)
+              analysis->states.contents[j]
             );
             j++;
           }
@@ -1265,12 +1265,12 @@ static void ts_query__perform_analysis(
       const TSSymbol parent_symbol = analysis_state__top(state)->parent_symbol;
       const TSFieldId parent_field_id = analysis_state__top(state)->field_id;
       const unsigned child_index = analysis_state__top(state)->child_index;
-      const QueryStep * const step = array_get(&self->steps, state->step_index);
+      const QueryStep * const step = &self->steps.contents[state->step_index];
 
       unsigned subgraph_index, exists;
       array_search_sorted_by(subgraphs, .symbol, parent_symbol, &subgraph_index, &exists);
       if (!exists) continue;
-      const AnalysisSubgraph *subgraph = array_get(subgraphs, subgraph_index);
+      const AnalysisSubgraph *subgraph = &subgraphs->contents[subgraph_index];
 
       // Follow every possible path in the parse table, but only visit states that
       // are part of the subgraph for the current symbol.
@@ -1306,8 +1306,7 @@ static void ts_query__perform_analysis(
           &node_index, &exists
         );
         while (node_index < subgraph->nodes.size) {
-          AnalysisSubgraphNode *node = array_get(&subgraph->nodes, node_index);
-          node_index++;
+          AnalysisSubgraphNode *node = &subgraph->nodes.contents[node_index++];
           if (node->state != successor.state || node->child_index != successor.child_index) break;
 
           // Use the subgraph to determine what alias and field will eventually be applied
@@ -1413,7 +1412,7 @@ static void ts_query__perform_analysis(
           if (does_match) {
             for (;;) {
               next_state.step_index++;
-              next_step = array_get(&self->steps, next_state.step_index);
+              next_step = &self->steps.contents[next_state.step_index];
               if (
                 next_step->depth == PATTERN_DONE_MARKER ||
                 next_step->depth <= step->depth
@@ -1437,7 +1436,7 @@ static void ts_query__perform_analysis(
             // record that matching can terminate at this step of the pattern. Otherwise,
             // add this state to the list of states to process on the next iteration.
             if (!next_step->is_dead_end) {
-              bool did_finish_pattern = array_get(&self->steps, next_state.step_index)->depth != step->depth;
+              bool did_finish_pattern = self->steps.contents[next_state.step_index].depth != step->depth;
               if (did_finish_pattern) {
                 array_insert_sorted_by(&analysis->finished_parent_symbols, , state->root_symbol);
               } else if (next_state.depth == 0) {
@@ -1457,7 +1456,7 @@ static void ts_query__perform_analysis(
               next_step->alternative_index > next_state.step_index
             ) {
               next_state.step_index = next_step->alternative_index;
-              next_step = array_get(&self->steps, next_state.step_index);
+              next_step = &self->steps.contents[next_state.step_index];
             } else {
               break;
             }
@@ -1475,9 +1474,9 @@ static void ts_query__perform_analysis(
 static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   Array(uint16_t) non_rooted_pattern_start_steps = array_new();
   for (unsigned i = 0; i < self->pattern_map.size; i++) {
-    PatternEntry *pattern = array_get(&self->pattern_map, i);
+    PatternEntry *pattern = &self->pattern_map.contents[i];
     if (!pattern->is_rooted) {
-      QueryStep *step = array_get(&self->steps, pattern->step_index);
+      QueryStep *step = &self->steps.contents[pattern->step_index];
       if (step->symbol != WILDCARD_SYMBOL) {
         array_push(&non_rooted_pattern_start_steps, i);
       }
@@ -1489,7 +1488,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   // captures, and record the indices of all of the steps that have child steps.
   Array(uint32_t) parent_step_indices = array_new();
   for (unsigned i = 0; i < self->steps.size; i++) {
-    QueryStep *step = array_get(&self->steps, i);
+    QueryStep *step = &self->steps.contents[i];
     if (step->depth == PATTERN_DONE_MARKER) {
       step->parent_pattern_guaranteed = true;
       step->root_pattern_guaranteed = true;
@@ -1500,7 +1499,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
     bool is_wildcard = step->symbol == WILDCARD_SYMBOL;
     step->contains_captures = step->capture_ids[0] != NONE;
     for (unsigned j = i + 1; j < self->steps.size; j++) {
-      QueryStep *next_step = array_get(&self->steps, j);
+      QueryStep *next_step = &self->steps.contents[j];
       if (
         next_step->depth == PATTERN_DONE_MARKER ||
         next_step->depth <= step->depth
@@ -1530,8 +1529,8 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   // parent.
   AnalysisSubgraphArray subgraphs = array_new();
   for (unsigned i = 0; i < parent_step_indices.size; i++) {
-    uint32_t parent_step_index = *array_get(&parent_step_indices, i);
-    TSSymbol parent_symbol = array_get(&self->steps, parent_step_index)->symbol;
+    uint32_t parent_step_index = parent_step_indices.contents[i];
+    TSSymbol parent_symbol = self->steps.contents[parent_step_index].symbol;
     AnalysisSubgraph subgraph = { .symbol = parent_symbol };
     array_insert_sorted_by(&subgraphs, .symbol, subgraph);
   }
@@ -1573,7 +1572,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
                 &exists
               );
               if (exists) {
-                AnalysisSubgraph *subgraph = array_get(&subgraphs, subgraph_index);
+                AnalysisSubgraph *subgraph = &subgraphs.contents[subgraph_index];
                 if (subgraph->nodes.size == 0 || array_back(&subgraph->nodes)->state != state) {
                   array_push(&subgraph->nodes, ((AnalysisSubgraphNode) {
                     .state = state,
@@ -1610,7 +1609,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
               &exists
             );
             if (exists) {
-              AnalysisSubgraph *subgraph = array_get(&subgraphs, subgraph_index);
+              AnalysisSubgraph *subgraph = &subgraphs.contents[subgraph_index];
               if (
                 subgraph->start_states.size == 0 ||
                 *array_back(&subgraph->start_states) != state
@@ -1627,7 +1626,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   // from the end states using the predecessor map.
   Array(AnalysisSubgraphNode) next_nodes = array_new();
   for (unsigned i = 0; i < subgraphs.size; i++) {
-    AnalysisSubgraph *subgraph = array_get(&subgraphs, i);
+    AnalysisSubgraph *subgraph = &subgraphs.contents[i];
     if (subgraph->nodes.size == 0) {
       array_delete(&subgraph->start_states);
       array_erase(&subgraphs, i);
@@ -1668,16 +1667,16 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   #ifdef DEBUG_ANALYZE_QUERY
     printf("\nSubgraphs:\n");
     for (unsigned i = 0; i < subgraphs.size; i++) {
-      AnalysisSubgraph *subgraph = array_get(&subgraphs, i);
+      AnalysisSubgraph *subgraph = &subgraphs.contents[i];
       printf("  %u, %s:\n", subgraph->symbol, ts_language_symbol_name(self->language, subgraph->symbol));
       for (unsigned j = 0; j < subgraph->start_states.size; j++) {
         printf(
           "    {state: %u}\n",
-          *array_get(&subgraph->start_states, j)
+          subgraph->start_states.contents[j]
         );
       }
       for (unsigned j = 0; j < subgraph->nodes.size; j++) {
-        AnalysisSubgraphNode *node = array_get(&subgraph->nodes, j);
+        AnalysisSubgraphNode *node = &subgraph->nodes.contents[j];
         printf(
           "    {state: %u, child_index: %u, production_id: %u, done: %d}\n",
           node->state, node->child_index, node->production_id, node->done
@@ -1692,9 +1691,9 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   bool all_patterns_are_valid = true;
   QueryAnalysis analysis = query_analysis__new();
   for (unsigned i = 0; i < parent_step_indices.size; i++) {
-    uint16_t parent_step_index = *array_get(&parent_step_indices, i);
-    uint16_t parent_depth = array_get(&self->steps, parent_step_index)->depth;
-    TSSymbol parent_symbol = array_get(&self->steps, parent_step_index)->symbol;
+    uint16_t parent_step_index = parent_step_indices.contents[i];
+    uint16_t parent_depth = self->steps.contents[parent_step_index].depth;
+    TSSymbol parent_symbol = self->steps.contents[parent_step_index].symbol;
     if (parent_symbol == ts_builtin_sym_error) continue;
 
     // Find the subgraph that corresponds to this pattern's root symbol. If the pattern's
@@ -1706,18 +1705,18 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
       uint32_t j, child_exists;
       array_search_sorted_by(&self->step_offsets, .step_index, first_child_step_index, &j, &child_exists);
       ts_assert(child_exists);
-      *error_offset = array_get(&self->step_offsets, j)->byte_offset;
+      *error_offset = self->step_offsets.contents[j].byte_offset;
       all_patterns_are_valid = false;
       break;
     }
 
     // Initialize an analysis state at every parse state in the table where
     // this parent symbol can occur.
-    AnalysisSubgraph *subgraph = array_get(&subgraphs, subgraph_index);
+    AnalysisSubgraph *subgraph = &subgraphs.contents[subgraph_index];
     analysis_state_set__clear(&analysis.states, &analysis.state_pool);
     analysis_state_set__clear(&analysis.deeper_states, &analysis.state_pool);
     for (unsigned j = 0; j < subgraph->start_states.size; j++) {
-      TSStateId parse_state = *array_get(&subgraph->start_states, j);
+      TSStateId parse_state = subgraph->start_states.contents[j];
       analysis_state_set__push(&analysis.states, &analysis.state_pool, &((AnalysisState) {
         .step_index = parent_step_index + 1,
         .stack = {
@@ -1737,7 +1736,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
     #ifdef DEBUG_ANALYZE_QUERY
       printf(
         "\nWalk states for %s:\n",
-        ts_language_symbol_name(self->language, (*array_get(&analysis.states, 0))->stack[0].parent_symbol)
+        ts_language_symbol_name(self->language, analysis.states.contents[0]->stack[0].parent_symbol)
       );
     #endif
 
@@ -1748,7 +1747,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
     // be considered fallible.
     if (analysis.did_abort) {
       for (unsigned j = parent_step_index + 1; j < self->steps.size; j++) {
-        QueryStep *step = array_get(&self->steps, j);
+        QueryStep *step = &self->steps.contents[j];
         if (
           step->depth <= parent_depth ||
           step->depth == PATTERN_DONE_MARKER
@@ -1769,7 +1768,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
       uint32_t j, impossible_exists;
       array_search_sorted_by(&self->step_offsets, .step_index, impossible_step_index, &j, &impossible_exists);
       if (j >= self->step_offsets.size) j = self->step_offsets.size - 1;
-      *error_offset = array_get(&self->step_offsets, j)->byte_offset;
+      *error_offset = self->step_offsets.contents[j].byte_offset;
       all_patterns_are_valid = false;
       break;
     }
@@ -1777,8 +1776,8 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
     // Mark as fallible any step where a match terminated.
     // Later, this property will be propagated to all of the step's predecessors.
     for (unsigned j = 0; j < analysis.final_step_indices.size; j++) {
-      uint32_t final_step_index = *array_get(&analysis.final_step_indices, j);
-      QueryStep *step = array_get(&self->steps, final_step_index);
+      uint32_t final_step_index = analysis.final_step_indices.contents[j];
+      QueryStep *step = &self->steps.contents[final_step_index];
       if (
         step->depth != PATTERN_DONE_MARKER &&
         step->depth > parent_depth &&
@@ -1793,7 +1792,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   // Mark as indefinite any step with captures that are used in predicates.
   Array(uint16_t) predicate_capture_ids = array_new();
   for (unsigned i = 0; i < self->patterns.size; i++) {
-    QueryPattern *pattern = array_get(&self->patterns, i);
+    QueryPattern *pattern = &self->patterns.contents[i];
 
     // Gather all of the captures that are used in predicates for this pattern.
     array_clear(&predicate_capture_ids);
@@ -1802,7 +1801,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
       end = start + pattern->predicate_steps.length,
       j = start; j < end; j++
     ) {
-      TSQueryPredicateStep *step = array_get(&self->predicate_steps, j);
+      TSQueryPredicateStep *step = &self->predicate_steps.contents[j];
       if (step->type == TSQueryPredicateStepTypeCapture) {
         uint16_t value_id = step->value_id;
         array_insert_sorted_by(&predicate_capture_ids, , value_id);
@@ -1815,7 +1814,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
       end = start + pattern->steps.length,
       j = start; j < end; j++
     ) {
-      QueryStep *step = array_get(&self->steps, j);
+      QueryStep *step = &self->steps.contents[j];
       for (unsigned k = 0; k < MAX_STEP_CAPTURE_COUNT; k++) {
         uint16_t capture_id = step->capture_ids[k];
         if (capture_id == NONE) break;
@@ -1835,7 +1834,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   while (!done) {
     done = true;
     for (unsigned i = self->steps.size - 1; i > 0; i--) {
-      QueryStep *step = array_get(&self->steps, i);
+      QueryStep *step = &self->steps.contents[i];
       if (step->depth == PATTERN_DONE_MARKER) continue;
 
       // Determine if this step is definite or has definite alternatives.
@@ -1848,12 +1847,12 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
         if (step->alternative_index == NONE || step->alternative_index < i) {
           break;
         }
-        step = array_get(&self->steps, step->alternative_index);
+        step = &self->steps.contents[step->alternative_index];
       }
 
       // If not, mark its predecessor as indefinite.
       if (!parent_pattern_guaranteed) {
-        QueryStep *prev_step = array_get(&self->steps, i - 1);
+        QueryStep *prev_step = &self->steps.contents[i - 1];
         if (
           !prev_step->is_dead_end &&
           prev_step->depth != PATTERN_DONE_MARKER &&
@@ -1869,7 +1868,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   #ifdef DEBUG_ANALYZE_QUERY
     printf("Steps:\n");
     for (unsigned i = 0; i < self->steps.size; i++) {
-      QueryStep *step = array_get(&self->steps, i);
+      QueryStep *step = &self->steps.contents[i];
       if (step->depth == PATTERN_DONE_MARKER) {
         printf("  %u: DONE\n", i);
       } else {
@@ -1893,18 +1892,18 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
   // prevent certain optimizations with range restrictions.
   analysis.did_abort = false;
   for (uint32_t i = 0; i < non_rooted_pattern_start_steps.size; i++) {
-    uint16_t pattern_entry_index = *array_get(&non_rooted_pattern_start_steps, i);
-    PatternEntry *pattern_entry = array_get(&self->pattern_map, pattern_entry_index);
+    uint16_t pattern_entry_index = non_rooted_pattern_start_steps.contents[i];
+    PatternEntry *pattern_entry = &self->pattern_map.contents[pattern_entry_index];
 
     analysis_state_set__clear(&analysis.states, &analysis.state_pool);
     analysis_state_set__clear(&analysis.deeper_states, &analysis.state_pool);
     for (unsigned j = 0; j < subgraphs.size; j++) {
-      AnalysisSubgraph *subgraph = array_get(&subgraphs, j);
+      AnalysisSubgraph *subgraph = &subgraphs.contents[j];
       TSSymbolMetadata metadata = ts_language_symbol_metadata(self->language, subgraph->symbol);
       if (metadata.visible || metadata.named) continue;
 
       for (uint32_t k = 0; k < subgraph->start_states.size; k++) {
-        TSStateId parse_state = *array_get(&subgraph->start_states, k);
+        TSStateId parse_state = subgraph->start_states.contents[k];
         analysis_state_set__push(&analysis.states, &analysis.state_pool, &((AnalysisState) {
           .step_index = pattern_entry->step_index,
           .stack = {
@@ -1933,11 +1932,11 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
     );
 
     if (analysis.finished_parent_symbols.size > 0) {
-      array_get(&self->patterns, pattern_entry->pattern_index)->is_non_local = true;
+      self->patterns.contents[pattern_entry->pattern_index].is_non_local = true;
     }
 
     for (unsigned k = 0; k < analysis.finished_parent_symbols.size; k++) {
-      TSSymbol symbol = *array_get(&analysis.finished_parent_symbols, k);
+      TSSymbol symbol = analysis.finished_parent_symbols.contents[k];
       array_insert_sorted_by(&self->repeat_symbols_with_rootless_patterns, , symbol);
     }
   }
@@ -1947,7 +1946,7 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
       printf("\nRepetition symbols with rootless patterns:\n");
       printf("aborted analysis: %d\n", analysis.did_abort);
       for (unsigned i = 0; i < self->repeat_symbols_with_rootless_patterns.size; i++) {
-        TSSymbol symbol = *array_get(&self->repeat_symbols_with_rootless_patterns, i);
+        TSSymbol symbol = self->repeat_symbols_with_rootless_patterns.contents[i];
         printf("  %u, %s\n", symbol, ts_language_symbol_name(self->language, symbol));
       }
       printf("\n");
@@ -1956,8 +1955,8 @@ static bool ts_query__analyze_patterns(TSQuery *self, unsigned *error_offset) {
 
   // Cleanup
   for (unsigned i = 0; i < subgraphs.size; i++) {
-    array_delete(&array_get(&subgraphs, i)->start_states);
-    array_delete(&array_get(&subgraphs, i)->nodes);
+    array_delete(&subgraphs.contents[i].start_states);
+    array_delete(&subgraphs.contents[i].nodes);
   }
   array_delete(&subgraphs);
   query_analysis__delete(&analysis);
@@ -1976,7 +1975,7 @@ static void ts_query__add_negated_fields(
   TSFieldId *field_ids,
   uint16_t field_count
 ) {
-  QueryStep *step = array_get(&self->steps, step_index);
+  QueryStep *step = &self->steps.contents[step_index];
 
   // The negated field array stores a list of field lists, separated by zeros.
   // Try to find the start index of an existing list that matches this new list.
@@ -1984,7 +1983,7 @@ static void ts_query__add_negated_fields(
   unsigned match_count = 0;
   unsigned start_i = 0;
   for (unsigned i = 0; i < self->negated_fields.size; i++) {
-    TSFieldId existing_field_id = *array_get(&self->negated_fields, i);
+    TSFieldId existing_field_id = self->negated_fields.contents[i];
 
     // At each zero value, terminate the match attempt. If we've exactly
     // matched the new field list, then reuse this index. Otherwise,
@@ -2254,10 +2253,10 @@ static TSQueryError ts_query__parse_pattern(
     // For all of the branches except for the last one, add the subsequent branch as an
     // alternative, and link the end of the branch to the current end of the steps.
     for (unsigned i = 0; i < branch_step_indices.size - 1; i++) {
-      uint32_t step_index = *array_get(&branch_step_indices, i);
-      uint32_t next_step_index = *array_get(&branch_step_indices, i + 1);
-      QueryStep *start_step = array_get(&self->steps, step_index);
-      QueryStep *end_step = array_get(&self->steps, next_step_index - 1);
+      uint32_t step_index = branch_step_indices.contents[i];
+      uint32_t next_step_index = branch_step_indices.contents[i + 1];
+      QueryStep *start_step = &self->steps.contents[step_index];
+      QueryStep *end_step = &self->steps.contents[next_step_index - 1];
       start_step->alternative_index = next_step_index;
       end_step->alternative_index = self->steps.size;
       end_step->is_dead_end = true;
@@ -2537,13 +2536,13 @@ static TSQueryError ts_query__parse_pattern(
                 last_child_step->alternative_index != NONE &&
                 last_child_step->alternative_index < self->steps.size
               ) {
-                QueryStep *alternative_step = array_get(&self->steps, last_child_step->alternative_index);
+                QueryStep *alternative_step = &self->steps.contents[last_child_step->alternative_index];
                 alternative_step->is_last_child = true;
                 while (
                   alternative_step->alternative_index != NONE &&
                   alternative_step->alternative_index < self->steps.size
                 ) {
-                  alternative_step = array_get(&self->steps, alternative_step->alternative_index);
+                  alternative_step = &self->steps.contents[alternative_step->alternative_index];
                   alternative_step->is_last_child = true;
                 }
               }
@@ -2649,7 +2648,7 @@ static TSQueryError ts_query__parse_pattern(
     }
 
     uint32_t step_index = starting_step_index;
-    QueryStep *step = array_get(&self->steps, step_index);
+    QueryStep *step = &self->steps.contents[step_index];
     for (;;) {
       step->field = field_id;
       if (
@@ -2658,7 +2657,7 @@ static TSQueryError ts_query__parse_pattern(
         step->alternative_index < self->steps.size
       ) {
         step_index = step->alternative_index;
-        step = array_get(&self->steps, step_index);
+        step = &self->steps.contents[step_index];
       } else {
         break;
       }
@@ -2707,9 +2706,9 @@ static TSQueryError ts_query__parse_pattern(
       // Stop when `step->alternative_index` is `NONE` or it points to
       // `repeat_step` or beyond. Note that having just been pushed,
       // `repeat_step` occupies slot `self->steps.size - 1`.
-      QueryStep *step = array_get(&self->steps, starting_step_index);
+      QueryStep *step = &self->steps.contents[starting_step_index];
       while (step->alternative_index != NONE && step->alternative_index < self->steps.size - 1) {
-        step = array_get(&self->steps, step->alternative_index);
+        step = &self->steps.contents[step->alternative_index];
       }
       step->alternative_index = self->steps.size;
     }
@@ -2721,9 +2720,9 @@ static TSQueryError ts_query__parse_pattern(
       stream_advance(stream);
       stream_skip_whitespace(stream);
 
-      QueryStep *step = array_get(&self->steps, starting_step_index);
+      QueryStep *step = &self->steps.contents[starting_step_index];
       while (step->alternative_index != NONE && step->alternative_index < self->steps.size) {
-        step = array_get(&self->steps, step->alternative_index);
+        step = &self->steps.contents[step->alternative_index];
       }
       step->alternative_index = self->steps.size;
     }
@@ -2749,7 +2748,7 @@ static TSQueryError ts_query__parse_pattern(
 
       uint32_t step_index = starting_step_index;
       for (;;) {
-        QueryStep *step = array_get(&self->steps, step_index);
+        QueryStep *step = &self->steps.contents[step_index];
         query_step__add_capture(step, capture_id);
         if (
           step->alternative_index != NONE &&
@@ -2847,14 +2846,14 @@ TSQuery *ts_query_new(
     // Maintain a map that can look up patterns for a given root symbol.
     uint16_t wildcard_root_alternative_index = NONE;
     for (;;) {
-      QueryStep *step = array_get(&self->steps, start_step_index);
+      QueryStep *step = &self->steps.contents[start_step_index];
 
       // If a pattern has a wildcard at its root, but it has a non-wildcard child,
       // then optimize the matching process by skipping matching the wildcard.
       // Later, during the matching process, the query cursor will check that
       // there is a parent node, and capture it if necessary.
       if (step->symbol == WILDCARD_SYMBOL && step->depth == 0 && !step->field) {
-        QueryStep *second_step = array_get(&self->steps, start_step_index + 1);
+        QueryStep *second_step = &self->steps.contents[start_step_index + 1];
         if (second_step->symbol != WILDCARD_SYMBOL && second_step->depth == 1 && !second_step->is_immediate) {
           wildcard_root_alternative_index = step->alternative_index;
           start_step_index += 1;
@@ -2869,7 +2868,7 @@ TSQuery *ts_query_new(
       uint32_t start_depth = step->depth;
       bool is_rooted = start_depth == 0;
       for (uint32_t step_index = start_step_index + 1; step_index < self->steps.size; step_index++) {
-        QueryStep *child_step = array_get(&self->steps, step_index);
+        QueryStep *child_step = &self->steps.contents[step_index];
         if (child_step->is_dead_end) break;
         if (child_step->depth == start_depth) {
           is_rooted = false;
@@ -2973,24 +2972,24 @@ const TSQueryPredicateStep *ts_query_predicates_for_pattern(
   uint32_t pattern_index,
   uint32_t *step_count
 ) {
-  Slice slice = array_get(&self->patterns, pattern_index)->predicate_steps;
+  Slice slice = self->patterns.contents[pattern_index].predicate_steps;
   *step_count = slice.length;
   if (slice.length == 0) return NULL;
-  return array_get(&self->predicate_steps, slice.offset);
+  return &self->predicate_steps.contents[slice.offset];
 }
 
 uint32_t ts_query_start_byte_for_pattern(
   const TSQuery *self,
   uint32_t pattern_index
 ) {
-  return array_get(&self->patterns, pattern_index)->start_byte;
+  return self->patterns.contents[pattern_index].start_byte;
 }
 
 uint32_t ts_query_end_byte_for_pattern(
   const TSQuery *self,
   uint32_t pattern_index
 ) {
-  return array_get(&self->patterns, pattern_index)->end_byte;
+  return self->patterns.contents[pattern_index].end_byte;
 }
 
 bool ts_query_is_pattern_rooted(
@@ -2998,7 +2997,7 @@ bool ts_query_is_pattern_rooted(
   uint32_t pattern_index
 ) {
   for (unsigned i = 0; i < self->pattern_map.size; i++) {
-    PatternEntry *entry = array_get(&self->pattern_map, i);
+    PatternEntry *entry = &self->pattern_map.contents[i];
     if (entry->pattern_index == pattern_index) {
       if (!entry->is_rooted) return false;
     }
@@ -3011,7 +3010,7 @@ bool ts_query_is_pattern_non_local(
   uint32_t pattern_index
 ) {
   if (pattern_index < self->patterns.size) {
-    return array_get(&self->patterns, pattern_index)->is_non_local;
+    return self->patterns.contents[pattern_index].is_non_local;
   } else {
     return false;
   }
@@ -3023,12 +3022,12 @@ bool ts_query_is_pattern_guaranteed_at_step(
 ) {
   uint32_t step_index = UINT32_MAX;
   for (unsigned i = 0; i < self->step_offsets.size; i++) {
-    StepOffset *step_offset = array_get(&self->step_offsets, i);
+    StepOffset *step_offset = &self->step_offsets.contents[i];
     if (step_offset->byte_offset > byte_offset) break;
     step_index = step_offset->step_index;
   }
   if (step_index < self->steps.size) {
-    return array_get(&self->steps, step_index)->root_pattern_guaranteed;
+    return self->steps.contents[step_index].root_pattern_guaranteed;
   } else {
     return false;
   }
@@ -3039,8 +3038,8 @@ bool ts_query__step_is_fallible(
   uint16_t step_index
 ) {
   ts_assert((uint32_t)step_index + 1 < self->steps.size);
-  QueryStep *step = array_get(&self->steps, step_index);
-  QueryStep *next_step = array_get(&self->steps, step_index + 1);
+  QueryStep *step = &self->steps.contents[step_index];
+  QueryStep *next_step = &self->steps.contents[step_index + 1];
   return (
     next_step->depth != PATTERN_DONE_MARKER &&
     next_step->depth > step->depth &&
@@ -3058,7 +3057,7 @@ void ts_query_disable_capture(
   int id = symbol_table_id_for_name(&self->captures, name, length);
   if (id != -1) {
     for (unsigned i = 0; i < self->steps.size; i++) {
-      QueryStep *step = array_get(&self->steps, i);
+      QueryStep *step = &self->steps.contents[i];
       query_step__remove_capture(step, id);
     }
   }
@@ -3071,7 +3070,7 @@ void ts_query_disable_pattern(
   // Remove the given pattern from the pattern map. Its steps will still
   // be in the `steps` array, but they will never be read.
   for (unsigned i = 0; i < self->pattern_map.size; i++) {
-    PatternEntry *pattern = array_get(&self->pattern_map, i);
+    PatternEntry *pattern = &self->pattern_map.contents[i];
     if (pattern->pattern_index == pattern_index) {
       array_erase(&self->pattern_map, i);
       i--;
@@ -3148,7 +3147,7 @@ void ts_query_cursor_exec(
   if (query) {
     LOG("query steps:\n");
     for (unsigned i = 0; i < query->steps.size; i++) {
-      QueryStep *step = array_get(&query->steps, i);
+      QueryStep *step = &query->steps.contents[i];
       LOG("  %u: {", i);
       if (step->depth == PATTERN_DONE_MARKER) {
         LOG("DONE");
@@ -3253,7 +3252,7 @@ static bool ts_query_cursor__first_in_progress_capture(
   *byte_offset = UINT32_MAX;
   *pattern_index = UINT32_MAX;
   for (unsigned i = 0; i < self->states.size; i++) {
-    QueryState *state = array_get(&self->states, i);
+    QueryState *state = &self->states.contents[i];
     if (state->dead) continue;
 
     const CaptureList *captures = capture_list_pool_get(
@@ -3264,7 +3263,7 @@ static bool ts_query_cursor__first_in_progress_capture(
       continue;
     }
 
-    TSNode node = array_get(captures, state->consumed_capture_count)->node;
+    TSNode node = captures->contents[state->consumed_capture_count].node;
     if (
       ts_node_end_byte(node) <= self->start_byte ||
       point_lte(ts_node_end_point(node), self->start_point)
@@ -3280,7 +3279,7 @@ static bool ts_query_cursor__first_in_progress_capture(
       node_start_byte < *byte_offset ||
       (node_start_byte == *byte_offset && state->pattern_index < *pattern_index)
     ) {
-      QueryStep *step = array_get(&self->query->steps, state->step_index);
+      QueryStep *step = &self->query->steps.contents[state->step_index];
       if (is_definite) {
         // We're being a bit conservative here by asserting that the following step
         // is not immediate, because this capture might end up being discarded if the
@@ -3336,8 +3335,8 @@ void ts_query_cursor__compare_captures(
   for (;;) {
     if (i < left_captures->size) {
       if (j < right_captures->size) {
-        TSQueryCapture *left = array_get(left_captures, i);
-        TSQueryCapture *right = array_get(right_captures, j);
+        TSQueryCapture *left = &left_captures->contents[i];
+        TSQueryCapture *right = &right_captures->contents[j];
         if (left->node.id == right->node.id && left->index == right->index) {
           i++;
           j++;
@@ -3376,7 +3375,7 @@ static void ts_query_cursor__add_state(
   TSQueryCursor *self,
   const PatternEntry *pattern
 ) {
-  QueryStep *step = array_get(&self->query->steps, pattern->step_index);
+  QueryStep *step = &self->query->steps.contents[pattern->step_index];
   uint32_t start_depth = self->depth - step->depth;
 
   // Keep the states array in ascending order of start_depth and pattern_index,
@@ -3400,7 +3399,7 @@ static void ts_query_cursor__add_state(
   // need to execute in order to keep the states ordered by pattern_index.
   uint32_t index = self->states.size;
   while (index > 0) {
-    QueryState *prev_state = array_get(&self->states, index - 1);
+    QueryState *prev_state = &self->states.contents[index - 1];
     if (prev_state->start_depth < start_depth) break;
     if (prev_state->start_depth == start_depth) {
       // Avoid inserting an unnecessary duplicate state, which would be
@@ -3464,7 +3463,7 @@ static CaptureList *ts_query_cursor__prepare_to_capture(
           "  abandon state. index:%u, pattern:%u, offset:%u.\n",
           state_index, pattern_index, byte_offset
         );
-        QueryState *other_state = array_get(&self->states, state_index);
+        QueryState *other_state = &self->states.contents[state_index];
         state->capture_list_id = other_state->capture_list_id;
         other_state->capture_list_id = NONE;
         other_state->dead = true;
@@ -3534,8 +3533,8 @@ static QueryState *ts_query_cursor__copy_state(
   }
 
   array_insert(&self->states, state_index + 1, copy);
-  *state_ref = array_get(&self->states, state_index);
-  return array_get(&self->states, state_index + 1);
+  *state_ref = &self->states.contents[state_index];
+  return &self->states.contents[state_index + 1];
 }
 
 static inline bool ts_query_cursor__should_descend(
@@ -3550,8 +3549,8 @@ static inline bool ts_query_cursor__should_descend(
   // If there are in-progress matches whose remaining steps occur
   // deeper in the tree, then descend.
   for (unsigned i = 0; i < self->states.size; i++) {
-    QueryState *state = array_get(&self->states, i);
-    QueryStep *next_step = array_get(&self->query->steps, state->step_index);
+    QueryState *state = &self->states.contents[i];;
+    QueryStep *next_step = &self->query->steps.contents[state->step_index];
     if (
       next_step->depth != PATTERN_DONE_MARKER &&
       state->start_depth + next_step->depth > self->depth
@@ -3645,8 +3644,8 @@ static inline bool ts_query_cursor__advance(
         // After leaving a node, remove any states that cannot make further progress.
         uint32_t deleted_count = 0;
         for (unsigned i = 0, n = self->states.size; i < n; i++) {
-          QueryState *state = array_get(&self->states, i);
-          QueryStep *step = array_get(&self->query->steps, state->step_index);
+          QueryState *state = &self->states.contents[i];
+          QueryStep *step = &self->query->steps.contents[state->step_index];
 
           // If a state completed its pattern inside of this node, but was deferred from finishing
           // in order to search for longer matches, mark it as finished.
@@ -3679,7 +3678,7 @@ static inline bool ts_query_cursor__advance(
           }
 
           else if (deleted_count > 0) {
-            *array_get(&self->states, i - deleted_count) = *state;
+            self->states.contents[i - deleted_count] = *state;
           }
         }
         self->states.size -= deleted_count;
@@ -3782,11 +3781,11 @@ static inline bool ts_query_cursor__advance(
         // Add new states for any patterns whose root node is a wildcard.
         if (!node_is_error) {
           for (unsigned i = 0; i < self->query->wildcard_root_pattern_count; i++) {
-            PatternEntry *pattern = array_get(&self->query->pattern_map, i);
+            PatternEntry *pattern = &self->query->pattern_map.contents[i];
 
             // If this node matches the first step of the pattern, then add a new
             // state at the start of this pattern.
-            QueryStep *step = array_get(&self->query->steps, pattern->step_index);
+            QueryStep *step = &self->query->steps.contents[pattern->step_index];
             uint32_t start_depth = self->depth - step->depth;
             if (
               (pattern->is_rooted ?
@@ -3804,9 +3803,9 @@ static inline bool ts_query_cursor__advance(
         // Add new states for any patterns whose root node matches this node.
         unsigned i;
         if (ts_query__pattern_map_search(self->query, symbol, &i)) {
-          PatternEntry *pattern = array_get(&self->query->pattern_map, i);
+          PatternEntry *pattern = &self->query->pattern_map.contents[i];
 
-          QueryStep *step = array_get(&self->query->steps, pattern->step_index);
+          QueryStep *step = &self->query->steps.contents[pattern->step_index];
           uint32_t start_depth = self->depth - step->depth;
           do {
             // If this node matches the first step of the pattern, then add a new
@@ -3824,15 +3823,15 @@ static inline bool ts_query_cursor__advance(
             // Advance to the next pattern whose root node matches this node.
             i++;
             if (i == self->query->pattern_map.size) break;
-            pattern = array_get(&self->query->pattern_map, i);
-            step = array_get(&self->query->steps, pattern->step_index);
+            pattern = &self->query->pattern_map.contents[i];
+            step = &self->query->steps.contents[pattern->step_index];
           } while (step->symbol == symbol);
         }
 
         // Update all of the in-progress states with current node.
         for (unsigned j = 0, copy_count = 0; j < self->states.size; j += 1 + copy_count) {
-          QueryState *state = array_get(&self->states, j);
-          QueryStep *step = array_get(&self->query->steps, state->step_index);
+          QueryState *state = &self->states.contents[j];
+          QueryStep *step = &self->query->steps.contents[state->step_index];
           state->has_in_progress_alternatives = false;
           copy_count = 0;
 
@@ -3881,7 +3880,7 @@ static inline bool ts_query_cursor__advance(
           }
 
           if (step->negated_field_list_id) {
-            TSFieldId *negated_field_ids = array_get(&self->query->negated_fields, step->negated_field_list_id);
+            TSFieldId *negated_field_ids = &self->query->negated_fields.contents[step->negated_field_list_id];
             for (;;) {
               TSFieldId negated_field_id = *negated_field_ids;
               if (negated_field_id) {
@@ -3982,7 +3981,7 @@ static inline bool ts_query_cursor__advance(
             state->step_index
           );
 
-          QueryStep *next_step = array_get(&self->query->steps, state->step_index);
+          QueryStep *next_step = &self->query->steps.contents[state->step_index];
 
           // For a given step, if the current symbol is the wildcard symbol, `_`, and it is **not**
           // named, meaning it should capture anonymous nodes, **and** the next step is immediate,
@@ -4005,8 +4004,8 @@ static inline bool ts_query_cursor__advance(
           // so this is an interactive process.
           unsigned end_index = j + 1;
           for (unsigned k = j; k < end_index; k++) {
-            QueryState *child_state = array_get(&self->states, k);
-            QueryStep *child_step = array_get(&self->query->steps, child_state->step_index);
+            QueryState *child_state = &self->states.contents[k];
+            QueryStep *child_step = &self->query->steps.contents[child_state->step_index];
             if (child_step->alternative_index != NONE) {
               // A "dead-end" step exists only to add a non-sequential jump into the step sequence,
               // via its alternative index. When a state reaches a dead-end step, it jumps straight
@@ -4047,7 +4046,7 @@ static inline bool ts_query_cursor__advance(
         }
 
         for (unsigned j = 0; j < self->states.size; j++) {
-          QueryState *state = array_get(&self->states, j);
+          QueryState *state = &self->states.contents[j];
           if (state->dead) {
             array_erase(&self->states, j);
             j--;
@@ -4059,7 +4058,7 @@ static inline bool ts_query_cursor__advance(
           // one state has a strict subset of another state's captures.
           bool did_remove = false;
           for (unsigned k = j + 1; k < self->states.size; k++) {
-            QueryState *other_state = array_get(&self->states, k);
+            QueryState *other_state = &self->states.contents[k];
 
             // Query states are kept in ascending order of start_depth and pattern_index.
             // Since the longest-match criteria is only used for deduping matches of the same
@@ -4119,7 +4118,7 @@ static inline bool ts_query_cursor__advance(
               state->step_index,
               capture_list_pool_get(&self->capture_list_pool, state->capture_list_id)->size
             );
-            QueryStep *next_step = array_get(&self->query->steps, state->step_index);
+            QueryStep *next_step = &self->query->steps.contents[state->step_index];
             if (next_step->depth == PATTERN_DONE_MARKER) {
               if (state->has_in_progress_alternatives) {
                 LOG("  defer finishing pattern %u\n", state->pattern_index);
@@ -4164,7 +4163,7 @@ bool ts_query_cursor_next_match(
     }
   }
 
-  QueryState *state = array_get(&self->finished_states, 0);
+  QueryState *state = &self->finished_states.contents[0];
   if (state->id == UINT32_MAX) state->id = self->next_state_id++;
   match->id = state->id;
   match->pattern_index = state->pattern_index;
@@ -4184,7 +4183,7 @@ void ts_query_cursor_remove_match(
   uint32_t match_id
 ) {
   for (unsigned i = 0; i < self->finished_states.size; i++) {
-    const QueryState *state = array_get(&self->finished_states, i);
+    const QueryState *state = &self->finished_states.contents[i];
     if (state->id == match_id) {
       capture_list_pool_release(
         &self->capture_list_pool,
@@ -4198,7 +4197,7 @@ void ts_query_cursor_remove_match(
   // Remove unfinished query states as well to prevent future
   // captures for a match being removed.
   for (unsigned i = 0; i < self->states.size; i++) {
-    const QueryState *state = array_get(&self->states, i);
+    const QueryState *state = &self->states.contents[i];
     if (state->id == match_id) {
       capture_list_pool_release(
         &self->capture_list_pool,
@@ -4238,7 +4237,7 @@ bool ts_query_cursor_next_capture(
     uint32_t first_finished_capture_byte = first_unfinished_capture_byte;
     uint32_t first_finished_pattern_index = first_unfinished_pattern_index;
     for (unsigned i = 0; i < self->finished_states.size;) {
-      QueryState *state = array_get(&self->finished_states, i);
+      QueryState *state = &self->finished_states.contents[i];
       const CaptureList *captures = capture_list_pool_get(
         &self->capture_list_pool,
         state->capture_list_id
@@ -4254,7 +4253,7 @@ bool ts_query_cursor_next_capture(
         continue;
       }
 
-      TSNode node = array_get(captures, state->consumed_capture_count)->node;
+      TSNode node = captures->contents[state->consumed_capture_count].node;
 
       bool node_precedes_range = (
         ts_node_end_byte(node) <= self->start_byte ||
@@ -4294,7 +4293,7 @@ bool ts_query_cursor_next_capture(
     if (first_finished_state) {
       state = first_finished_state;
     } else if (first_unfinished_state_is_definite) {
-      state = array_get(&self->states, first_unfinished_state_index);
+      state = &self->states.contents[first_unfinished_state_index];
     } else {
       state = NULL;
     }
@@ -4323,7 +4322,7 @@ bool ts_query_cursor_next_capture(
       );
       capture_list_pool_release(
         &self->capture_list_pool,
-        array_get(&self->states, first_unfinished_state_index)->capture_list_id
+        self->states.contents[first_unfinished_state_index].capture_list_id
       );
       array_erase(&self->states, first_unfinished_state_index);
     }
diff --git a/lib/src/stack.c b/lib/src/stack.c
index 9142007..453d984 100644
--- a/lib/src/stack.c
+++ b/lib/src/stack.c
@@ -290,8 +290,8 @@ static StackVersion ts_stack__add_version(
 ) {
   StackHead head = {
     .node = node,
-    .node_count_at_last_error = array_get(&self->heads, original_version)->node_count_at_last_error,
-    .last_external_token = array_get(&self->heads, original_version)->last_external_token,
+    .node_count_at_last_error = self->heads.contents[original_version].node_count_at_last_error,
+    .last_external_token = self->heads.contents[original_version].last_external_token,
     .status = StackStatusActive,
     .lookahead_when_paused = NULL_SUBTREE,
   };
@@ -308,8 +308,8 @@ static void ts_stack__add_slice(
   SubtreeArray *subtrees
 ) {
   for (uint32_t i = self->slices.size - 1; i + 1 > 0; i--) {
-    StackVersion version = array_get(&self->slices, i)->version;
-    if (array_get(&self->heads, version)->node == node) {
+    StackVersion version = self->slices.contents[i].version;
+    if (self->heads.contents[version].node == node) {
       StackSlice slice = {*subtrees, version};
       array_insert(&self->slices, i + 1, slice);
       return;
@@ -349,7 +349,7 @@ static StackSliceArray stack__iter(
 
   while (self->iterators.size > 0) {
     for (uint32_t i = 0, size = self->iterators.size; i < size; i++) {
-      StackIterator *iterator = array_get(&self->iterators, i);
+      StackIterator *iterator = &self->iterators.contents[i];
       StackNode *node = iterator->node;
 
       StackAction action = callback(payload, iterator);
@@ -384,11 +384,11 @@ static StackSliceArray stack__iter(
         StackLink link;
         if (j == node->link_count) {
           link = node->links[0];
-          next_iterator = array_get(&self->iterators, i);
+          next_iterator = &self->iterators.contents[i];
         } else {
           if (self->iterators.size >= MAX_ITERATOR_COUNT) continue;
           link = node->links[j];
-          StackIterator current_iterator = *array_get(&self->iterators, i);
+          StackIterator current_iterator = self->iterators.contents[i];
           array_push(&self->iterators, current_iterator);
           next_iterator = array_back(&self->iterators);
           ts_subtree_array_copy(next_iterator->subtrees, &next_iterator->subtrees);
@@ -444,12 +444,12 @@ void ts_stack_delete(Stack *self) {
     array_delete(&self->iterators);
   stack_node_release(self->base_node, &self->node_pool, self->subtree_pool);
   for (uint32_t i = 0; i < self->heads.size; i++) {
-    stack_head_delete(array_get(&self->heads, i), &self->node_pool, self->subtree_pool);
+    stack_head_delete(&self->heads.contents[i], &self->node_pool, self->subtree_pool);
   }
   array_clear(&self->heads);
   if (self->node_pool.contents) {
     for (uint32_t i = 0; i < self->node_pool.size; i++)
-      ts_free(*array_get(&self->node_pool, i));
+      ts_free(self->node_pool.contents[i]);
     array_delete(&self->node_pool);
   }
   array_delete(&self->heads);
@@ -552,8 +552,8 @@ forceinline StackAction pop_pending_callback(void *payload, const StackIterator
 StackSliceArray ts_stack_pop_pending(Stack *self, StackVersion version) {
   StackSliceArray pop = stack__iter(self, version, pop_pending_callback, NULL, 0);
   if (pop.size > 0) {
-    ts_stack_renumber_version(self, array_get(&pop, 0)->version, version);
-    array_get(&pop, 0)->version = version;
+    ts_stack_renumber_version(self, pop.contents[0].version, version);
+    pop.contents[0].version = version;
   }
   return pop;
 }
@@ -561,7 +561,7 @@ StackSliceArray ts_stack_pop_pending(Stack *self, StackVersion version) {
 forceinline StackAction pop_error_callback(void *payload, const StackIterator *iterator) {
   if (iterator->subtrees.size > 0) {
     bool *found_error = payload;
-    if (!*found_error && ts_subtree_is_error(*array_get(&iterator->subtrees, 0))) {
+    if (!*found_error && ts_subtree_is_error(iterator->subtrees.contents[0])) {
       *found_error = true;
       return StackActionPop | StackActionStop;
     } else {
@@ -580,8 +580,8 @@ SubtreeArray ts_stack_pop_error(Stack *self, StackVersion version) {
       StackSliceArray pop = stack__iter(self, version, pop_error_callback, &found_error, 1);
       if (pop.size > 0) {
         ts_assert(pop.size == 1);
-        ts_stack_renumber_version(self, array_get(&pop, 0)->version, version);
-        return array_get(&pop, 0)->subtrees;
+        ts_stack_renumber_version(self, pop.contents[0].version, version);
+        return pop.contents[0].subtrees;
       }
       break;
     }
@@ -609,7 +609,7 @@ forceinline StackAction summarize_stack_callback(void *payload, const StackItera
   unsigned depth = iterator->subtree_count;
   if (depth > session->max_depth) return StackActionStop;
   for (unsigned i = session->summary->size - 1; i + 1 > 0; i--) {
-    StackSummaryEntry entry = *array_get(session->summary, i);
+    StackSummaryEntry entry = session->summary->contents[i];
     if (entry.depth < depth) break;
     if (entry.depth == depth && entry.state == state) return StackActionNone;
   }
@@ -628,7 +628,7 @@ void ts_stack_record_summary(Stack *self, StackVersion version, unsigned max_dep
   };
   array_init(session.summary);
   stack__iter(self, version, summarize_stack_callback, &session, -1);
-  StackHead *head = array_get(&self->heads, version);
+  StackHead *head = &self->heads.contents[version];
   if (head->summary) {
     array_delete(head->summary);
     ts_free(head->summary);
@@ -677,8 +677,8 @@ void ts_stack_renumber_version(Stack *self, StackVersion v1, StackVersion v2) {
   if (v1 == v2) return;
   ts_assert(v2 < v1);
   ts_assert((uint32_t)v1 < self->heads.size);
-  StackHead *source_head = array_get(&self->heads, v1);
-  StackHead *target_head = array_get(&self->heads, v2);
+  StackHead *source_head = &self->heads.contents[v1];
+  StackHead *target_head = &self->heads.contents[v2];
   if (target_head->summary && !source_head->summary) {
     source_head->summary = target_head->summary;
     target_head->summary = NULL;
@@ -689,15 +689,14 @@ void ts_stack_renumber_version(Stack *self, StackVersion v1, StackVersion v2) {
 }
 
 void ts_stack_swap_versions(Stack *self, StackVersion v1, StackVersion v2) {
-  StackHead temporary_head = *array_get(&self->heads, v1);
-  *array_get(&self->heads, v1) = *array_get(&self->heads, v2);
-  *array_get(&self->heads, v2) = temporary_head;
+  StackHead temporary_head = self->heads.contents[v1];
+  self->heads.contents[v1] = self->heads.contents[v2];
+  self->heads.contents[v2] = temporary_head;
 }
 
 StackVersion ts_stack_copy_version(Stack *self, StackVersion version) {
   ts_assert(version < self->heads.size);
-  StackHead version_head = *array_get(&self->heads, version);
-  array_push(&self->heads, version_head);
+  array_push(&self->heads, self->heads.contents[version]);
   StackHead *head = array_back(&self->heads);
   stack_node_retain(head->node);
   if (head->last_external_token.ptr) ts_subtree_retain(head->last_external_token);
@@ -707,8 +706,8 @@ StackVersion ts_stack_copy_version(Stack *self, StackVersion version) {
 
 bool ts_stack_merge(Stack *self, StackVersion version1, StackVersion version2) {
   if (!ts_stack_can_merge(self, version1, version2)) return false;
-  StackHead *head1 = array_get(&self->heads, version1);
-  StackHead *head2 = array_get(&self->heads, version2);
+  StackHead *head1 = &self->heads.contents[version1];
+  StackHead *head2 = &self->heads.contents[version2];
   for (uint32_t i = 0; i < head2->node->link_count; i++) {
     stack_node_add_link(head1->node, head2->node->links[i], self->subtree_pool);
   }
@@ -720,8 +719,8 @@ bool ts_stack_merge(Stack *self, StackVersion version1, StackVersion version2) {
 }
 
 bool ts_stack_can_merge(Stack *self, StackVersion version1, StackVersion version2) {
-  StackHead *head1 = array_get(&self->heads, version1);
-  StackHead *head2 = array_get(&self->heads, version2);
+  StackHead *head1 = &self->heads.contents[version1];
+  StackHead *head2 = &self->heads.contents[version2];
   return
     head1->status == StackStatusActive &&
     head2->status == StackStatusActive &&
@@ -766,7 +765,7 @@ Subtree ts_stack_resume(Stack *self, StackVersion version) {
 void ts_stack_clear(Stack *self) {
   stack_node_retain(self->base_node);
   for (uint32_t i = 0; i < self->heads.size; i++) {
-    stack_head_delete(array_get(&self->heads, i), &self->node_pool, self->subtree_pool);
+    stack_head_delete(&self->heads.contents[i], &self->node_pool, self->subtree_pool);
   }
   array_clear(&self->heads);
   array_push(&self->heads, ((StackHead) {
@@ -789,7 +788,7 @@ bool ts_stack_print_dot_graph(Stack *self, const TSLanguage *language, FILE *f)
 
   array_clear(&self->iterators);
   for (uint32_t i = 0; i < self->heads.size; i++) {
-    StackHead *head = array_get(&self->heads, i);
+    StackHead *head = &self->heads.contents[i];
     if (head->status == StackStatusHalted) continue;
 
     fprintf(f, "node_head_%u [shape=none, label=\"\"]\n", i);
@@ -807,7 +806,7 @@ bool ts_stack_print_dot_graph(Stack *self, const TSLanguage *language, FILE *f)
 
     if (head->summary) {
       fprintf(f, "\nsummary:");
-      for (uint32_t j = 0; j < head->summary->size; j++) fprintf(f, " %u", array_get(head->summary, j)->state);
+      for (uint32_t j = 0; j < head->summary->size; j++) fprintf(f, " %u", head->summary->contents[j].state);
     }
 
     if (head->last_external_token.ptr) {
@@ -828,11 +827,11 @@ bool ts_stack_print_dot_graph(Stack *self, const TSLanguage *language, FILE *f)
     all_iterators_done = true;
 
     for (uint32_t i = 0; i < self->iterators.size; i++) {
-      StackIterator iterator = *array_get(&self->iterators, i);
+      StackIterator iterator = self->iterators.contents[i];
       StackNode *node = iterator.node;
 
       for (uint32_t j = 0; j < visited_nodes.size; j++) {
-        if (*array_get(&visited_nodes, j) == node) {
+        if (visited_nodes.contents[j] == node) {
           node = NULL;
           break;
         }
@@ -891,7 +890,7 @@ bool ts_stack_print_dot_graph(Stack *self, const TSLanguage *language, FILE *f)
 
         StackIterator *next_iterator;
         if (j == 0) {
-          next_iterator = array_get(&self->iterators, i);
+          next_iterator = &self->iterators.contents[i];
         } else {
           array_push(&self->iterators, iterator);
           next_iterator = array_back(&self->iterators);
diff --git a/lib/src/subtree.c b/lib/src/subtree.c
index 97d55c8..35f6573 100644
--- a/lib/src/subtree.c
+++ b/lib/src/subtree.c
@@ -73,14 +73,14 @@ void ts_subtree_array_copy(SubtreeArray self, SubtreeArray *dest) {
     dest->contents = ts_calloc(self.capacity, sizeof(Subtree));
     memcpy(dest->contents, self.contents, self.size * sizeof(Subtree));
     for (uint32_t i = 0; i < self.size; i++) {
-      ts_subtree_retain(*array_get(dest, i));
+      ts_subtree_retain(dest->contents[i]);
     }
   }
 }
 
 void ts_subtree_array_clear(SubtreePool *pool, SubtreeArray *self) {
   for (uint32_t i = 0; i < self->size; i++) {
-    ts_subtree_release(pool, *array_get(self, i));
+    ts_subtree_release(pool, self->contents[i]);
   }
   array_clear(self);
 }
@@ -96,7 +96,7 @@ void ts_subtree_array_remove_trailing_extras(
 ) {
   array_clear(destination);
   while (self->size > 0) {
-    Subtree last = *array_get(self, self->size - 1);
+    Subtree last = self->contents[self->size - 1];
     if (ts_subtree_extra(last)) {
       self->size--;
       array_push(destination, last);
@@ -110,9 +110,9 @@ void ts_subtree_array_remove_trailing_extras(
 void ts_subtree_array_reverse(SubtreeArray *self) {
   for (uint32_t i = 0, limit = self->size / 2; i < limit; i++) {
     size_t reverse_index = self->size - 1 - i;
-    Subtree swap = *array_get(self, i);
-    *array_get(self, i) = *array_get(self, reverse_index);
-    *array_get(self, reverse_index) = swap;
+    Subtree swap = self->contents[i];
+    self->contents[i] = self->contents[reverse_index];
+    self->contents[reverse_index] = swap;
   }
 }
 
@@ -127,7 +127,7 @@ SubtreePool ts_subtree_pool_new(uint32_t capacity) {
 void ts_subtree_pool_delete(SubtreePool *self) {
   if (self->free_trees.contents) {
     for (unsigned i = 0; i < self->free_trees.size; i++) {
-      ts_free(array_get(&self->free_trees, i)->ptr);
+      ts_free(self->free_trees.contents[i].ptr);
     }
     array_delete(&self->free_trees);
   }
diff --git a/lib/src/tree_cursor.c b/lib/src/tree_cursor.c
index 70ef5e3..561c163 100644
--- a/lib/src/tree_cursor.c
+++ b/lib/src/tree_cursor.c
@@ -16,11 +16,11 @@ typedef struct {
 // CursorChildIterator
 
 static inline bool ts_tree_cursor_is_entry_visible(const TreeCursor *self, uint32_t index) {
-  TreeCursorEntry *entry = array_get(&self->stack, index);
+  TreeCursorEntry *entry = &self->stack.contents[index];
   if (index == 0 || ts_subtree_visible(*entry->subtree)) {
     return true;
   } else if (!ts_subtree_extra(*entry->subtree)) {
-    TreeCursorEntry *parent_entry = array_get(&self->stack, index - 1);
+    TreeCursorEntry *parent_entry = &self->stack.contents[index - 1];
     return ts_language_alias_at(
       self->tree->language,
       parent_entry->subtree->ptr->production_id,
@@ -374,7 +374,7 @@ TreeCursorStep ts_tree_cursor_goto_previous_sibling_internal(TSTreeCursor *_self
     return step;
 
   // restore position from the parent node
-  const TreeCursorEntry *parent = array_get(&self->stack, self->stack.size - 2);
+  const TreeCursorEntry *parent = &self->stack.contents[self->stack.size - 2];
   Length position = parent->position;
   uint32_t child_index = array_back(&self->stack)->child_index;
   const Subtree *children = ts_subtree_children((*(parent->subtree)));
@@ -425,7 +425,7 @@ void ts_tree_cursor_goto_descendant(
   // Ascend to the lowest ancestor that contains the goal node.
   for (;;) {
     uint32_t i = self->stack.size - 1;
-    TreeCursorEntry *entry = array_get(&self->stack, i);
+    TreeCursorEntry *entry = &self->stack.contents[i];
     uint32_t next_descendant_index =
       entry->descendant_index +
       (ts_tree_cursor_is_entry_visible(self, i) ? 1 : 0) +
@@ -479,7 +479,7 @@ TSNode ts_tree_cursor_current_node(const TSTreeCursor *_self) {
   bool is_extra = ts_subtree_extra(*last_entry->subtree);
   TSSymbol alias_symbol = is_extra ? 0 : self->root_alias_symbol;
   if (self->stack.size > 1 && !is_extra) {
-    TreeCursorEntry *parent_entry = array_get(&self->stack, self->stack.size - 2);
+    TreeCursorEntry *parent_entry = &self->stack.contents[self->stack.size - 2];
     alias_symbol = ts_language_alias_at(
       self->tree->language,
       parent_entry->subtree->ptr->production_id,
@@ -516,8 +516,8 @@ void ts_tree_cursor_current_status(
   // Walk up the tree, visiting the current node and its invisible ancestors,
   // because fields can refer to nodes through invisible *wrapper* nodes,
   for (unsigned i = self->stack.size - 1; i > 0; i--) {
-    TreeCursorEntry *entry = array_get(&self->stack, i);
-    TreeCursorEntry *parent_entry = array_get(&self->stack, i - 1);
+    TreeCursorEntry *entry = &self->stack.contents[i];
+    TreeCursorEntry *parent_entry = &self->stack.contents[i - 1];
 
     const TSSymbol *alias_sequence = ts_language_alias_sequence(
       self->tree->language,
@@ -630,11 +630,11 @@ uint32_t ts_tree_cursor_current_depth(const TSTreeCursor *_self) {
 TSNode ts_tree_cursor_parent_node(const TSTreeCursor *_self) {
   const TreeCursor *self = (const TreeCursor *)_self;
   for (int i = (int)self->stack.size - 2; i >= 0; i--) {
-    TreeCursorEntry *entry = array_get(&self->stack, i);
+    TreeCursorEntry *entry = &self->stack.contents[i];
     bool is_visible = true;
     TSSymbol alias_symbol = 0;
     if (i > 0) {
-      TreeCursorEntry *parent_entry = array_get(&self->stack, i - 1);
+      TreeCursorEntry *parent_entry = &self->stack.contents[i - 1];
       alias_symbol = ts_language_alias_at(
         self->tree->language,
         parent_entry->subtree->ptr->production_id,
@@ -659,8 +659,8 @@ TSFieldId ts_tree_cursor_current_field_id(const TSTreeCursor *_self) {
 
   // Walk up the tree, visiting the current node and its invisible ancestors.
   for (unsigned i = self->stack.size - 1; i > 0; i--) {
-    TreeCursorEntry *entry = array_get(&self->stack, i);
-    TreeCursorEntry *parent_entry = array_get(&self->stack, i - 1);
+    TreeCursorEntry *entry = &self->stack.contents[i];
+    TreeCursorEntry *parent_entry = &self->stack.contents[i - 1];
 
     // Stop walking up when another visible node is found.
     if (
diff --git a/lib/src/wasm_store.c b/lib/src/wasm_store.c
index f0c0970..47ac683 100644
--- a/lib/src/wasm_store.c
+++ b/lib/src/wasm_store.c
@@ -947,7 +947,7 @@ void ts_wasm_store_delete(TSWasmStore *self) {
   wasmtime_store_delete(self->store);
   wasm_engine_delete(self->engine);
   for (unsigned i = 0; i < self->language_instances.size; i++) {
-    LanguageWasmInstance *instance = array_get(&self->language_instances, i);
+    LanguageWasmInstance *instance = &self->language_instances.contents[i];
     language_id_delete(instance->language_id);
   }
   array_delete(&self->language_instances);
@@ -957,7 +957,7 @@ void ts_wasm_store_delete(TSWasmStore *self) {
 size_t ts_wasm_store_language_count(const TSWasmStore *self) {
   size_t result = 0;
   for (unsigned i = 0; i < self->language_instances.size; i++) {
-    const WasmLanguageId *id = array_get(&self->language_instances, i)->language_id;
+    const WasmLanguageId *id = self->language_instances.contents[i].language_id;
     if (!id->is_language_deleted) {
       result++;
     }
@@ -1451,7 +1451,7 @@ const TSLanguage *ts_wasm_store_load_language(
 
   // Clear out any instances of languages that have been deleted.
   for (unsigned i = 0; i < self->language_instances.size; i++) {
-    WasmLanguageId *id = array_get(&self->language_instances, i)->language_id;
+    WasmLanguageId *id = self->language_instances.contents[i].language_id;
     if (id->is_language_deleted) {
       language_id_delete(id);
       array_erase(&self->language_instances, i);
@@ -1492,7 +1492,7 @@ bool ts_wasm_store_add_language(
   // instances of languages that have been deleted.
   bool exists = false;
   for (unsigned i = 0; i < self->language_instances.size; i++) {
-    WasmLanguageId *id = array_get(&self->language_instances, i)->language_id;
+    WasmLanguageId *id = self->language_instances.contents[i].language_id;
     if (id->is_language_deleted) {
       language_id_delete(id);
       array_erase(&self->language_instances, i);
@@ -1563,7 +1563,7 @@ bool ts_wasm_store_start(TSWasmStore *self, TSLexer *lexer, const TSLanguage *la
   uint32_t instance_index;
   if (!ts_wasm_store_add_language(self, language, &instance_index)) return false;
   self->current_lexer = lexer;
-  self->current_instance = array_get(&self->language_instances, instance_index);
+  self->current_instance = &self->language_instances.contents[instance_index];
   self->has_error = false;
   ts_wasm_store_reset_heap(self);
   return true;
