
1. effective "Differential Upgrade" patch algorithm with high compression rate 2. effective recovery algorithm support recovery firmware in blocks which has low memory consumption and wear-leveling strategies, especially suitable for embeded devices with low RAM. 3. add sample ota bootloader project, see: board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_bootloader_recovery 4. add sample application project for download firmware through http, see: board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_application_download_through_http 5. add sample application project for download firmware through qcloud explorer console, see: board\TencentOS_tiny_EVB_MX_Plus\KEIL\ota\ota_application_download_through_qcloud_iot_explorer 6. an OTA markdown document is pending
562 lines
13 KiB
C
562 lines
13 KiB
C
/*----------------------------------------------------------------------------
|
|
* Tencent is pleased to support the open source community by making TencentOS
|
|
* available.
|
|
*
|
|
* Copyright (C) 2019 THL A29 Limited, a Tencent company. All rights reserved.
|
|
* If you have downloaded a copy of the TencentOS binary from Tencent, please
|
|
* note that the TencentOS binary is licensed under the BSD 3-Clause License.
|
|
*
|
|
* If you have downloaded a copy of the TencentOS source code from Tencent,
|
|
* please note that TencentOS source code is licensed under the BSD 3-Clause
|
|
* License, except for the third-party components listed below which are
|
|
* subject to different license terms. Your integration of TencentOS into your
|
|
* own projects may require compliance with the BSD 3-Clause License, as well
|
|
* as the other licenses applicable to the third-party components included
|
|
* within TencentOS.
|
|
*---------------------------------------------------------------------------*/
|
|
|
|
#include "assert.h"
|
|
#include "stdio.h"
|
|
#include "stdlib.h"
|
|
#include "string.h"
|
|
#include "graph.h"
|
|
|
|
int graph_create(graph_t *graph, int vertexs_n)
|
|
{
|
|
int i = 0;
|
|
vertex_t *vertexs;
|
|
|
|
if (!graph || vertexs_n <= 0) {
|
|
return -1;
|
|
}
|
|
|
|
memset(graph, 0, sizeof(graph_t));
|
|
|
|
if ((vertexs = malloc(vertexs_n * sizeof(vertex_t))) == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
memset(vertexs, 0, vertexs_n * sizeof(vertex_t));
|
|
|
|
for (i = 0; i < vertexs_n; ++i) {
|
|
list_init(&vertexs[i].first_in);
|
|
list_init(&vertexs[i].first_out);
|
|
}
|
|
|
|
graph->edges_n = 0;
|
|
graph->vertexs_n = vertexs_n;
|
|
graph->vertexs = vertexs;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_destroy(graph_t *graph)
|
|
{
|
|
int i = 0;
|
|
vertex_t *vertex;
|
|
edge_t *edge, *tmp;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < graph->vertexs_n; ++i) {
|
|
vertex = &graph->vertexs[i];
|
|
LIST_FOR_EACH_ENTRY_SAFE(edge, tmp, edge_t, tail_list, &vertex->first_out) {
|
|
list_del(&edge->tail_list);
|
|
free(edge);
|
|
}
|
|
}
|
|
|
|
free(graph->vertexs);
|
|
|
|
graph->edges_n = 0;
|
|
graph->vertexs_n = 0;
|
|
graph->vertexs = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_edge_add(graph_t *graph, int tail_vertex, int head_vertex)
|
|
{
|
|
edge_t *edge;
|
|
vertex_t *tail_v, *head_v;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (tail_vertex >= graph->vertexs_n ||
|
|
head_vertex >= graph->vertexs_n ||
|
|
tail_vertex < 0 ||
|
|
head_vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if ((edge = malloc(sizeof(edge_t))) == NULL) {
|
|
return -1;
|
|
}
|
|
|
|
edge->head_vertex = head_vertex;
|
|
edge->tail_vertex = tail_vertex;
|
|
list_init(&edge->head_list);
|
|
list_init(&edge->tail_list);
|
|
|
|
tail_v = &graph->vertexs[tail_vertex];
|
|
head_v = &graph->vertexs[head_vertex];
|
|
|
|
list_add(&edge->tail_list, &tail_v->first_out);
|
|
++tail_v->out_degree;
|
|
|
|
list_add(&edge->head_list, &head_v->first_in);
|
|
++head_v->in_degree;
|
|
|
|
++graph->edges_n;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_edge_rmv(graph_t *graph, int tail_vertex, int head_vertex)
|
|
{
|
|
edge_t *edge, *tmp;
|
|
vertex_t *tail_v, *head_v;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (tail_vertex >= graph->vertexs_n ||
|
|
head_vertex >= graph->vertexs_n ||
|
|
tail_vertex < 0 ||
|
|
head_vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
tail_v = &graph->vertexs[tail_vertex];
|
|
head_v = &graph->vertexs[head_vertex];
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(edge, tmp, edge_t, tail_list, &tail_v->first_out) {
|
|
if (edge->tail_vertex == tail_vertex &&
|
|
edge->head_vertex == head_vertex) {
|
|
list_del(&edge->tail_list);
|
|
--tail_v->out_degree;
|
|
|
|
list_del(&edge->head_list);
|
|
--head_v->in_degree;
|
|
|
|
free(edge);
|
|
--graph->edges_n;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int graph_edge_rmv_by_tail(graph_t *graph, int tail_vertex, graph_edge_delete_cb del_cb, void *cb_arg)
|
|
{
|
|
edge_t *edge, *tmp;
|
|
vertex_t *tail_v, *head_v;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (tail_vertex >= graph->vertexs_n || tail_vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
tail_v = &graph->vertexs[tail_vertex];
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(edge, tmp, edge_t, tail_list, &tail_v->first_out) {
|
|
if (edge->tail_vertex == tail_vertex) {
|
|
list_del(&edge->tail_list);
|
|
--tail_v->out_degree;
|
|
|
|
list_del(&edge->head_list);
|
|
head_v = &graph->vertexs[edge->head_vertex];
|
|
--head_v->in_degree;
|
|
|
|
if (del_cb) {
|
|
del_cb(edge->tail_vertex, edge->head_vertex, cb_arg);
|
|
}
|
|
|
|
free(edge);
|
|
--graph->edges_n;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_edge_rmv_one_by_tail(graph_t *graph, int tail_vertex)
|
|
{
|
|
edge_t *edge, *tmp;
|
|
vertex_t *tail_v, *head_v;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (tail_vertex >= graph->vertexs_n || tail_vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
tail_v = &graph->vertexs[tail_vertex];
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(edge, tmp, edge_t, tail_list, &tail_v->first_out) {
|
|
if (edge->tail_vertex == tail_vertex) {
|
|
list_del(&edge->tail_list);
|
|
--tail_v->out_degree;
|
|
|
|
list_del(&edge->head_list);
|
|
head_v = &graph->vertexs[edge->head_vertex];
|
|
--head_v->in_degree;
|
|
|
|
free(edge);
|
|
--graph->edges_n;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int graph_edge_rmv_by_head(graph_t *graph, int head_vertex, graph_edge_delete_cb del_cb, void *cb_arg)
|
|
{
|
|
edge_t *edge, *tmp;
|
|
vertex_t *tail_v, *head_v;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (head_vertex >= graph->vertexs_n || head_vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
head_v = &graph->vertexs[head_vertex];
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(edge, tmp, edge_t, head_list, &head_v->first_in) {
|
|
if (edge->head_vertex == head_vertex) {
|
|
list_del(&edge->tail_list);
|
|
tail_v = &graph->vertexs[edge->tail_vertex];
|
|
--tail_v->out_degree;
|
|
|
|
list_del(&edge->head_list);
|
|
--head_v->in_degree;
|
|
|
|
if (del_cb) {
|
|
del_cb(edge->tail_vertex, edge->head_vertex, cb_arg);
|
|
}
|
|
|
|
free(edge);
|
|
--graph->edges_n;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_edge_rmv_one_by_head(graph_t *graph, int head_vertex)
|
|
{
|
|
edge_t *edge, *tmp;
|
|
vertex_t *tail_v, *head_v;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (head_vertex >= graph->vertexs_n || head_vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
head_v = &graph->vertexs[head_vertex];
|
|
|
|
LIST_FOR_EACH_ENTRY_SAFE(edge, tmp, edge_t, head_list, &head_v->first_in) {
|
|
if (edge->head_vertex == head_vertex) {
|
|
list_del(&edge->tail_list);
|
|
tail_v = &graph->vertexs[edge->tail_vertex];
|
|
--tail_v->out_degree;
|
|
|
|
list_del(&edge->head_list);
|
|
--head_v->in_degree;
|
|
|
|
free(edge);
|
|
--graph->edges_n;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int graph_edgesn_get(graph_t *graph)
|
|
{
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
return graph->edges_n;
|
|
}
|
|
|
|
int graph_vertex_max(graph_t *graph)
|
|
{
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
return graph->vertexs_n;
|
|
}
|
|
|
|
int graph_indegree_get(graph_t *graph, int vertex)
|
|
{
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (vertex >= graph->vertexs_n || vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return graph->vertexs[vertex].in_degree;
|
|
}
|
|
|
|
int graph_outdegree_get(graph_t *graph, int vertex)
|
|
{
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (vertex >= graph->vertexs_n || vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
return graph->vertexs[vertex].out_degree;
|
|
}
|
|
|
|
int graph_tag_set(graph_t *graph, int vertex, v_tag_t tag)
|
|
{
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (vertex >= graph->vertexs_n || vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
graph->vertexs[vertex].tag = tag;
|
|
return 0;
|
|
}
|
|
|
|
int graph_tag_reset(graph_t *graph, int vertex)
|
|
{
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
if (vertex >= graph->vertexs_n || vertex < 0) {
|
|
return -1;
|
|
}
|
|
|
|
graph->vertexs[vertex].tag = 0;
|
|
return 0;
|
|
}
|
|
|
|
v_tag_t graph_tag_get(graph_t *graph, int vertex)
|
|
{
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return (v_tag_t)-1;
|
|
}
|
|
|
|
if (vertex >= graph->vertexs_n || vertex < 0) {
|
|
return (v_tag_t)-1;
|
|
}
|
|
|
|
return graph->vertexs[vertex].tag;
|
|
}
|
|
|
|
int graph_dfs_create(dfs_t *dfs, graph_t *graph)
|
|
{
|
|
if (!dfs || !graph) {
|
|
return -1;
|
|
}
|
|
|
|
if (graph_vertex_max(graph) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
memset(dfs, 0, sizeof(dfs_t));
|
|
|
|
if (stack_create(&dfs->stack, graph_vertex_max(graph)) != 0) {
|
|
return 0;
|
|
}
|
|
|
|
if (stack_push(&dfs->stack, 0) != 0) {
|
|
// push the first vertex of the graph into the stack
|
|
return -1;
|
|
}
|
|
|
|
graph_vertex_set_visited(graph, 0);
|
|
|
|
dfs->graph = graph;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_dfs_destroy(dfs_t *dfs)
|
|
{
|
|
int i = 0;
|
|
|
|
if (!dfs || !dfs->graph) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < graph_vertex_max(dfs->graph); ++i) {
|
|
graph_tag_reset(dfs->graph, i);
|
|
}
|
|
|
|
stack_destroy(&dfs->stack);
|
|
|
|
dfs->graph = NULL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_dfs_has_next(dfs_t *dfs)
|
|
{
|
|
if (!dfs || !dfs->graph) {
|
|
return 0;
|
|
}
|
|
|
|
return !stack_is_empty(&dfs->stack);
|
|
}
|
|
|
|
int graph_dfs_ring_detect(dfs_t *dfs, int *ring_size)
|
|
{
|
|
int i = 0;
|
|
edge_t *edge;
|
|
graph_t *graph;
|
|
vertex_t *vertex;
|
|
int vertex_idx, the_ring_size = 0, is_found = 0;
|
|
|
|
assert(sizeof(int) == sizeof(element_type_t));
|
|
|
|
if (!dfs || !dfs->graph || !ring_size) {
|
|
return -1;
|
|
}
|
|
|
|
graph = dfs->graph;
|
|
|
|
while (!stack_is_empty(&dfs->stack)) {
|
|
vertex_idx = stack_top(&dfs->stack);
|
|
vertex = &graph->vertexs[vertex_idx];
|
|
|
|
is_found = 0;
|
|
|
|
LIST_FOR_EACH_ENTRY(edge, edge_t, tail_list, &vertex->first_out) {
|
|
if (!graph_vertex_is_visited(graph, edge->head_vertex)) {
|
|
// still fresh neighbors here
|
|
stack_push(&dfs->stack, edge->head_vertex);
|
|
graph_vertex_set_visited(graph, edge->head_vertex);
|
|
is_found = 1;
|
|
break;
|
|
}
|
|
|
|
if (graph_vertex_is_tnode(graph, edge->head_vertex) ||
|
|
graph_vertex_is_rnode(graph, edge->head_vertex)) {
|
|
// already detected as a ring or a terminal node
|
|
continue;
|
|
}
|
|
|
|
// set DFS_RING_NODE tag
|
|
graph_vertex_set_rnode(graph, edge->head_vertex);
|
|
|
|
// if reach here, a ring is detected
|
|
stack_peek_init(&dfs->stack);
|
|
while ((vertex_idx = stack_peek(&dfs->stack)) != -1) {
|
|
++the_ring_size;
|
|
if (vertex_idx == edge->head_vertex) {
|
|
*ring_size = the_ring_size;
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
// no fresh neighbors anymore
|
|
if (!is_found) {
|
|
stack_pop(&dfs->stack);
|
|
graph_vertex_set_tnode(graph, vertex_idx);
|
|
}
|
|
}
|
|
|
|
/* if here, graph may still has isolated nodes there: multi-connected-component in the graph */
|
|
for (i = 0; i < graph_vertex_max(graph); ++i) {
|
|
if (!graph_vertex_is_visited(graph, i)) {
|
|
stack_push(&dfs->stack, i);
|
|
graph_vertex_set_visited(graph, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return -1;
|
|
}
|
|
|
|
int graph_out_print(graph_t *graph)
|
|
{
|
|
int i = 0;
|
|
edge_t *edge;
|
|
vertex_t *vertex;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < graph->vertexs_n; ++i) {
|
|
vertex = &graph->vertexs[i];
|
|
|
|
if (vertex->out_degree == 0) {
|
|
continue;
|
|
}
|
|
|
|
printf("\nvertex: %d\n", i);
|
|
|
|
LIST_FOR_EACH_ENTRY(edge, edge_t, tail_list, &vertex->first_out) {
|
|
printf(" - %d -> %d\n", edge->tail_vertex, edge->head_vertex);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int graph_in_print(graph_t *graph)
|
|
{
|
|
int i = 0;
|
|
edge_t *edge;
|
|
vertex_t *vertex;
|
|
|
|
if (!graph || !graph->vertexs || !graph->vertexs_n) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < graph->vertexs_n; ++i) {
|
|
vertex = &graph->vertexs[i];
|
|
|
|
if (vertex->in_degree == 0) {
|
|
continue;
|
|
}
|
|
|
|
printf("\nvertex: %d\n", i);
|
|
|
|
LIST_FOR_EACH_ENTRY(edge, edge_t, head_list, &vertex->first_in) {
|
|
printf(" - %d -> %d\n", edge->tail_vertex, edge->head_vertex);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|