Graphviz draw linked list

Posted by robburne on Tue, 08 Mar 2022 03:56:07 +0100

Graphviz draw linked list

Writing purpose

Originally, I wanted to be lazy. For the drawing of linked list, I wanted to draw it on A4 paper, then take a photo as electronic data, and then store it. However, considering my font "catch chicken", I didn't want to save it in the photo album or upload the photo to a hardware medium for storage, It is also impossible to save the A4 value as a material (considering that the paper data cannot be carried with you, and it is impossible to bring all the paper data with notes with you), so through the study of this article, we can draw the basic linked list, and then integrate the pictures drawn by the software into the documents supporting markdown syntax, In this way, a more beautiful electronic data has been completed.

Development environment preparation

  • Visual Studio Code

  • install Graphviz , because my system is windows, I can choose the installation of Windows version. After installation, you need to add dot.com under the path of the environment variable of the system The path to the EXE executable.

  • Then install the corresponding support libraries and languages on Visual Studio Code

  • Node visualization preparation

    • Ctrl+Shift+P, find Graphviz:Open Preview to the Side in the open window, and click to open the window, as shown below

Code example analysis

Background introduction

Originally, I came across MessageQueue when learning the message mechanism of Android. I always wanted to know the specific implementation of "how messages enter the queue", so I checked its internal principle, mainly the enqueueMessage function in MessageQueue. After analysis, I found that a linked list is maintained inside, Compare the when value of the new message to be added to the linked list with the when value of other messages in the linked list, and then add it to the linked list according to the rules. The specific code is as follows:

1  boolean enqueueMessage(Message msg, long when) {
2          if (msg.target == null) {
3              throw new IllegalArgumentException("Message must have a target.");
4          }
5          if (msg.isInUse()) {
6              throw new IllegalStateException(msg + " This message is already in use.");
7          }
8
9          synchronized (this) {
10              if (mQuitting) {
11                  IllegalStateException e = new IllegalStateException(
12                          msg.target + " sending message to a Handler on a dead thread");
13                  Log.w(TAG, e.getMessage(), e);
14                  msg.recycle();
15                  return false;
16              }
17
18              msg.markInUse();
19              msg.when = when;
20              Message p = mMessages;
21              boolean needWake;
22              if (p == null || when == 0 || when < p.when) {
23                  // New head, wake up the event queue if blocked.
24                  msg.next = p;
25                  mMessages = msg;
26                  needWake = mBlocked;
27              } else {
28                  // Inserted within the middle of the queue.  Usually we don't have to wake
29                  // up the event queue unless there is a barrier at the head of the queue
30                  // and the message is the earliest asynchronous message in the queue.
31                  needWake = mBlocked && p.target == null && msg.isAsynchronous();
32                  Message prev;
33                  for (;;) {
34                      prev = p;
35                      p = p.next;
36                      if (p == null || when < p.when) {
37                          break;
38                      }
39                      if (needWake && p.isAsynchronous()) {
40                          needWake = false;
41                      }
42                  }
43                  msg.next = p; // invariant: p == prev.next
44                  prev.next = msg;
45              }
46  
47              // We can assume mPtr != 0 because mQuitting is false.
48              if (needWake) {
49                  nativeWake(mPtr);
50              }
51          }
52          return true;
53}

Among them, I focus on how messages enter the queue. Therefore, when looking at the code, I can ignore some status flags and exception checks, such as if (msg.target == null), if (msg.isInUse()), mqitting and MSG Markinuse (), needWake, etc. just focus on line19 to line45.

  • Note: mMessages is a member variable of type Message.

  • For Message class, we only need to pay attention to the following contents

    public final class Message implements Parcelable{
    	long when;
        Message next;
        // Other member variables and methods are omitted below
    }
    

    Draw it with Graphviz as follows:

    digraph {
        label = "\n msg";
        rankdir = LR; // Layout from left to right
        node [shape = record]; 
        //< S10 "{> next"}
        list [label = "<s0> when | <s1> next"] //Arrangement order of linked list elements: vertical
        listnode0 [label = "<s0>null"]; // Declare an empty node
    
        list:s1 -> listnode0 // Specifies the point of the node
    
    }
    
    

Next, let's look at the first part of enqueueMessage, that is, line22 to line27, which can be considered in three cases

  • p == null
  • when == 0
  • when < p.when

Consider three special cases first

Note: the following pink nodes represent our newly added nodes msg

p==null

The following is the execution of codes line20 to line26 when p==null.

  • At first, the nodes of mMessages are as follows:

  • After executing line24 in enqueueMessage, the situation is as follows:

  • After executing code line25, the situation is as follows:

when == 0 || when < p.when

The premise of this situation is that the condition of p == null is not tenable, so there is at least one node in mMessages. At the same time, both cases add the node msg to be inserted to the head of the linked list. Therefore, the analysis of new node insertion is divided into one node and multiple nodes 2 as follows:

  • The initial situation is as follows, which is divided into single node and multiple nodes:

  • After executing line24 in enqueueMessage, the situation is as follows:

  • After executing code line25, the situation is as follows:

Consider enqueueMessageline32 to line44

This situation refers to the situation that there is at least one node in mMessages and the node msg to be added is not added to the head of the linked list.

According to the analysis of "when = = 0 | when < p.when", we know that the insertion logic of single node and multi node is actually the same, so we use multi node method to analyze the code below.

In the for loop, line36 to line38 are the termination conditions of the for loop, indicating that the node msg to be added has been compared with the when variables of other nodes in the linked list. If the when value of MSG node is smaller than that of a node in the linked list, and a node has not been found in the linked list until the end of the linked list (its when value is larger or equal to that of MSG node), the loop will be ended.

The first execution of the for loop is used to draw the node diagram, and other execution can be analogized:

  • prev = p

  • p = p.next

At this time, assuming that the value of when of the node msg to be added meets the loop termination conditions of line36 to line38 in the for loop, we will jump out of the loop and execute line43 and line44. The node diagram is shown below

  • msg.next = p

  • prev.next = msg

reference resources

Topics: Android IDE graphviz