5
3

More than 3 years have passed since last update.

Jetpack ComposeのSlotTableがちょっと読めるようになるメモ

Last updated at Posted at 2021-07-11

普通にComposeをやる場合は絶対に知らなくていい情報です :warning:
SlotTableはJetpack Composeのデータ構造のコアとなる部分である認識です。

普通にやるとAndroidのテーマとか色々設定されちゃうので、自作したAppilerなどで最低限のComposeのコードのみの場合で実験してそのデータなどを載せています。

@Composable
private fun Node() {
    ComposeNode<Node, TextApplier>(
        factory = {
            Node(mutableListOf())
        },
        update = {
        },
    )
}

SlotTable.asString()の結果

androidx.compose.runtime.SlotTable@9bbea42
Group(0) key=100, nodes=1, size=7, slots=[0: {}]
 Group(1) key=1000, nodes=1, size=6
  Group(2) key=200, nodes=1, size=5 objectKey=OpaqueKey(key=provider)
   Group(3) key=-985533221, nodes=1, size=4, slots=[2: androidx.compose.runtime.RecomposeScopeImpl@76f0a53, androidx.compose.runtime.internal.ComposableLambdaImpl@e9edf90]
    Group(4) key=-1465440517, nodes=1, size=3, slots=[4: androidx.compose.runtime.RecomposeScopeImpl@b234089]
     Group(5) key=-2103251557, nodes=1, size=2 aux=C(ComposeNode):Composables.kt#9igjgp
      Group(6) key=125, nodes=0, size=1 node=Node(children=[])

SlotTableの構成要素

  • groups :IntArray

ここにほとんどすべての情報が入っていそう。ただIntArrayなので、インスタンスの情報についてはslotsが持っているみたい。
arrayの5個区切りで一つのGroupになっていて、layoutが決まっている。

An array to store group information that is stored as groups of Group_Fields_Size elements of the array. The groups array can be thought of as an array of an inline struct.

private const val Group_Fields_Size = 5

// Group layout
//  0             | 1             | 2             | 3             | 4             |
//  Key           | Group info    | Parent anchor | Size          | Data anchor   |

データ例

0 = 100 // key
1 = 1 // Group info
2 = -1 // Parent anchor
3 = 7 // Size
4 = 0 // Data anchor
5 = 1000 // key
6 = 1 // Group info
7 = 0 // Parent anchor
8 = 6 // Size
9 = 1 // Data anchor
10 = 200  // key
11 = 536870913 // *** Group info ***
12 = 1 // Parent anchor
13 = 5 // Size
14 = 1 // ***Data anchor***
15 = -985533221
16 = 1
17 = 2
18 = 4
19 = 2
20 = -1465440517
21 = 1
22 = 3
23 = 3
24 = 4
25 = -2103251557
26 = 268435457
27 = 4
28 = 2
29 = 5
30 = 125
31 = 1073741824
32 = 5
33 = 1
34 = 6
35 = 0
36 = 0
  • Group info

groupsの中に入っているgroup infoの情報。
これもフォーマットが決まっている。

// Group info is laid out as follows,
// 31 30 29 28_27 26 25 24_23 22 21 20_19 18 17 16__15 14 13 12_11 10 09 08_07 06 05 04_03 02 01 00
// 0  n  ks ds r |                                node count                                       |
// where n is set when the group represents a node
// where ks is whether the group has a object key slot
// where ds is whether the group has a group data slot
// where r is always 0 (future use)

// Parent anchor is a group anchor to the parent, as the group gap is moved this value is updated to
// refer to the parent.

// Slot count is the total number of group slots, including itself, occupied by the group.

// Data anchor is an anchor to the group data. The value is positive if it is before the data gap
// and it is negative if it is after the data gap. As gaps are moved, these values are updated.

// Masks and flags
private const val NodeBit_Mask = 0b0100_0000_0000_0000__0000_0000_0000_0000
private const val ObjectKey_Mask = 0b0010_0000_0000_0000__0000_0000_0000_0000
private const val ObjectKey_Shift = 29
private const val Aux_Mask = 0b0001_0000_0000_0000__0000_0000_0000_0000
private const val Aux_Shift = 28
private const val Slots_Shift = Aux_Shift
private const val NodeCount_Mask = 0b0000_0111_1111_1111__1111_1111_1111_1111
  • slots

ここに実際のデータが入ってるみたい。recomposeするときに使うRecomposeScopeImplも入っているみたい。

An array that stores the slots for a group. The slot elements for a group start at the offset returned by dataAnchor of groups and continue to the next group's slots or to slotsSize for the last group. When in a writer the dataAnchor is an anchor instead of an index as slots might contain a gap.

データ例

0 = {PersistentHashMap@19987}  size = 0
1 = {OpaqueKey@20060} "OpaqueKey(key=provider)"
2 = {RecomposeScopeImpl@20000} 
3 = {ComposableLambdaImpl@20009} 
4 = {RecomposeScopeImpl@19971} 
5 = "C(ComposeNode):Composables.kt#9igjgp"
6 = {Node@20062} "Node(children=[])"

少し解読してみる

例:

groupsの情報の3番目の要素

10 = 200  // key
11 = 536870913 // *** Group info ***
12 = 1 // Parent anchor
13 = 5 // Size
14 = 1 // Data anchor

Group infoを2進数に

>>> 536870913.toString(2)
res1: kotlin.String = 10_0000_0000_0000_0000_0000_0000_0001

private const val ObjectKey_Mask = 0b0010_0000_0000_0000__0000_0000_0000_0000
ObjectKey_Maskがヒット。

Data Ankerは1になっているのでslotの1を見に行く

14 = 1 // Data anchor
1 = {OpaqueKey@20060} "OpaqueKey(key=provider)"

asString()での結果にも入っている。

  Group(2) key=200, nodes=1, size=5 objectKey=OpaqueKey(key=provider)
5
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
3