/**
 * @brief       PGP Manager (Lbl Analyze)
 * @author      AXELL CORPORATION
 * @description PGP Manager Layer.
 * @note        none
 * @history     2017_02_22  初版
 * @history     2017_10_26  Ver2.0
 * @history     2019_03_08  [SDK2.2] PGPでのMALLOC/FREEの定義にプレフィックスを追加 (#2432)
 * @history     2019_12_27  [SDK3.0] PGPのラベリング解析関数が確保したリソースを解放する関数を追加 (#1951)
 * @history     2021_06_30  [SDK3.3] PGPライブラリでのラベリング結果のマージ不具合を修正 (#3350)
*/
/* DOM-IGNORE-BEGIN */
/*
 * This program was created by AXELL CORPORATION.
 * Copyright (C) 2017-2021 AXELL CORPORATION, all rights reserved.
 */
/* DOM-IGNORE-END */

#include <stdio.h>
#include "pgp/pgpmgr.h"
#include "AG903_common.h"

#define RELEASE(p) {if ((p) != NULL) AG903_PGP_COMMON_FREE(p);}

#define MAX_INDEX 63
#define MAX_AREA  1048575
#define SET_END   0x8
#define RES_END   0x7
#define MASK_ID   0x00000000000000ffULL
#define MASK_INDX 0x000000000003fc00ULL
#define MASK_SLIN 0x000000003ff00000ULL
#define MASK_ELIN 0x000000ffc0000000ULL
#define MASK_FRID 0x00ffff0000000000ULL
#define MASK_PORT 0x0f00000000000000ULL
#define MASK_GFLG 0xf000000000000000ULL
#define MASK_MINX 0x00000000000003ffULL
#define MASK_MINY 0x00000000000ffc00ULL
#define MASK_MAXX 0x000000003ff00000ULL
#define MASK_MAXY 0x000000ffc0000000ULL
#define MASK_AREA 0x0fffff0000000000ULL
#define MASK_PFLG 0xf000000000000000ULL
#define INT_MASK_ID   0x000000ff
#define INT_MASK_INDX 0x000000ff
#define INT_MASK_SLIN 0x000003ff
#define INT_MASK_ELIN 0x000003ff
#define INT_MASK_FRID 0x0000ffff
#define INT_MASK_PORT 0x0000000f
#define INT_MASK_GFLG 0x0000000f
#define INT_MASK_MINX 0x000003ff
#define INT_MASK_MINY 0x000003ff
#define INT_MASK_MAXX 0x000003ff
#define INT_MASK_MAXY 0x000003ff
#define INT_MASK_AREA 0x000fffff
#define INT_MASK_PFLG 0x0000000f
#define SHFT_ID   0
#define SHFT_INDX 10
#define SHFT_SLIN 20
#define SHFT_ELIN 30
#define SHFT_FRID 40
#define SHFT_PORT 56
#define SHFT_GFLG 60
#define SHFT_MINX 0
#define SHFT_MINY 10
#define SHFT_MAXX 20
#define SHFT_MAXY 30
#define SHFT_AREA 40
#define SHFT_PFLG 60
#define SEL_ID    0
#define SEL_INDX  1
#define SEL_SLIN  2
#define SEL_ELIN  3
#define SEL_FRID  4
#define SEL_PORT  5
#define SEL_GFLG  6
#define SEL_MINX  7
#define SEL_MINY  8
#define SEL_MAXX  9
#define SEL_MAXY  10
#define SEL_AREA  11
#define SEL_PFLG  12
#define F_INV     0
#define F_VAL     1
#define F_RED     2
#define INIT_CCON 0x4000000000000000ULL
#define MAX_LINE  1023
#define MIN_LINE  0
#define INDEX_SIZE 3   /* 1つのインデックスはTable、Connection、Connection(prev)の3つから成り立つ */

/* 連結リスト構造体の定義を行います */
typedef struct llist {
        int32_t  minx; /* minx                */
        int32_t  miny; /* miny                */
        int32_t  maxx; /* maxx                */
        int32_t  maxy; /* maxy                */
        int32_t  area; /* area                */
        int32_t  pflg; /* plfags              */
        uint64_t pcon; /* previous connection */
        struct llist *next; /* next address   */
} PgpMgrAnalyzeLBL;

/* 連結リストに追加します */
static PgpMgrAnalyzeLBL *append_llist(PgpMgrAnalyzeLBL **p, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy, int32_t area, int32_t pflg, uint64_t pcon)
{
    PgpMgrAnalyzeLBL *n = AG903_PGP_COMMON_MALLOC(sizeof(PgpMgrAnalyzeLBL));
    if (n != NULL) {
        n->next = *p;
        *p = n;
        n->minx = minx;
        n->miny = miny;
        n->maxx = maxx;
        n->maxy = maxy;
        n->area = area;
        n->pflg = pflg;
        n->pcon = pcon;
    }
    return n;
}

/* 連結リストを全削除します */
static void rm_llist(PgpMgrAnalyzeLBL **p)
{
    while (*p != NULL) {
        PgpMgrAnalyzeLBL *n = *p;
        *p = (*p)->next;
        AG903_PGP_COMMON_FREE(n);
    }
}

/* テーブルから任意のフィールドを抽出します */
static int32_t get_tbl(uint64_t tbl, uint32_t type)
{
    int32_t value;

    switch (type) {
      case SEL_ID  :
          value = (int32_t)((tbl >> SHFT_ID) & INT_MASK_ID);
          break;
      case SEL_INDX:
          value = (int32_t)((tbl >> SHFT_INDX) & INT_MASK_INDX);
          break;
      case SEL_SLIN:
          value = (int32_t)((tbl >> SHFT_SLIN) & INT_MASK_SLIN);
          break;
      case SEL_ELIN:
          value = (int32_t)((tbl >> SHFT_ELIN) & INT_MASK_ELIN);
          break;
      case SEL_FRID:
          value = (int32_t)((tbl >> SHFT_FRID) & INT_MASK_FRID);
          break;
      case SEL_PORT:
          value = (int32_t)((tbl >> SHFT_PORT) & INT_MASK_PORT);
          break;
      case SEL_GFLG:
          value = (int32_t)((tbl >> SHFT_GFLG) & INT_MASK_GFLG);
          break;
      case SEL_MINX:
          value = (int32_t)((tbl >> SHFT_MINX) & INT_MASK_MINX);
          break;
      case SEL_MINY:
          value = (int32_t)((tbl >> SHFT_MINY) & INT_MASK_MINY);
          break;
      case SEL_MAXX:
          value = (int32_t)((tbl >> SHFT_MAXX) & INT_MASK_MAXX);
          break;
      case SEL_MAXY:
          value = (int32_t)((tbl >> SHFT_MAXY) & INT_MASK_MAXY);
          break;
      case SEL_AREA:
          value = (int32_t)((tbl >> SHFT_AREA) & INT_MASK_AREA);
          break;
      case SEL_PFLG:
          value = (int32_t)((tbl >> SHFT_PFLG) & INT_MASK_PFLG);
          break;
      default:
          ASSERT(0);
          break;
    }
    return value;
}

/* Connectionから任意の1ビットを抽出します */
static int32_t get_con(uint64_t con, uint32_t index)
{
    int32_t value;
    value = (int32_t)((con >> index) & 0x1);
    return value;
}

static void mrg_two_p(uint64_t *dst, uint64_t *src)
{
    int32_t dminx = get_tbl(*dst, SEL_MINX);
    int32_t sminx = get_tbl(*src, SEL_MINX);
    int64_t minx  = (dminx > sminx) ? sminx : dminx;

    int32_t dminy = get_tbl(*dst, SEL_MINY);
    int32_t sminy = get_tbl(*src, SEL_MINY);
    int64_t miny  = (dminy > sminy) ? sminy : dminy;

    int32_t dmaxx = get_tbl(*dst, SEL_MAXX);
    int32_t smaxx = get_tbl(*src, SEL_MAXX);
    int64_t maxx  = (dmaxx < smaxx) ? smaxx : dmaxx;

    int32_t dmaxy  = get_tbl(*dst, SEL_MAXY);
    int32_t smaxy  = get_tbl(*src, SEL_MAXY);
    int64_t maxy   = (dmaxy < smaxy) ? smaxy : dmaxy;

    int32_t darea = get_tbl(*dst, SEL_AREA);
    int32_t sarea = get_tbl(*src, SEL_AREA);
    int64_t area  = darea + sarea;

    int32_t dpflg = get_tbl(*dst, SEL_PFLG);
    int32_t spflg = get_tbl(*src, SEL_PFLG);
    int64_t pflg  = dpflg | spflg;

    *dst =
        (minx<<SHFT_MINX & MASK_MINX) |
        (miny<<SHFT_MINY & MASK_MINY) |
        (maxx<<SHFT_MAXX & MASK_MAXX) |
        (maxy<<SHFT_MAXY & MASK_MAXY) |
        (area<<SHFT_AREA & MASK_AREA) |
        (pflg<<SHFT_PFLG & MASK_PFLG);
}

/* マージ判定の前処理として、処理中のテーブルのConnectionを加工します */
static void con_tbl(int32_t num_index, uint64_t *ccon, uint64_t *pcon, uint32_t *vflg, uint64_t *con_base)
{
    int32_t  i, j;

    /* Reserved領域をスキップ */
    con_base += 2;

    /* 作業用変数配列を初期化する */
    for (i = 0; i < MAX_INDEX; i++) {
        if (i < num_index) {
            *(ccon + i) = *(con_base + 2 * i);
            *(pcon + i) = *(con_base + 2 * i + 1);
            *(vflg + i) = F_VAL;
        } else {
            *(ccon + i) = 0;
            *(pcon + i) = 0;
            *(vflg + i) = F_INV;
        }
    }

    /* 連結情報をマージし重複エントリを除外します */
 retry:
    for (i = num_index-1; i >= 0; --i) { /* i:src index */
        for (j = num_index-1; j >= 0; --j) {    /* j:dest index */
            if ((i != j) && (ccon[i] != 0) && (ccon[j] & ccon[i])) {
                /* ここでは接続情報のみマージ */
                ccon[j] |= ccon[i];
                pcon[j] |= pcon[i];
                ASSERT(vflg[j] == F_VAL);

                /* マージ元のテーブルの値は後で参照されるため残す */
                ccon[i] = 0;
                pcon[i] = 0;
                vflg[i] = F_INV;

                goto retry;
            }
        }
    }

}

static void pack_tbl(uint64_t *dst, int32_t minx, int32_t miny, int32_t maxx, int32_t maxy, int32_t area, int32_t pflg)
{
    *dst =
        ((uint64_t)minx<<SHFT_MINX & MASK_MINX) |
        ((uint64_t)miny<<SHFT_MINY & MASK_MINY) |
        ((uint64_t)maxx<<SHFT_MAXX & MASK_MAXX) |
        ((uint64_t)maxy<<SHFT_MAXY & MASK_MAXY) |
        ((uint64_t)area<<SHFT_AREA & MASK_AREA) |
        ((uint64_t)pflg<<SHFT_PFLG & MASK_PFLG);
}

/* 前のテーブルとの接続情報を元にラベリング出力を直接編集しラベルが分割されなかった状態にします */
static void pcon_tbl(int32_t num_index, uint64_t *con_base, uint64_t *tbl_base, PgpMgrAnalyzeLBL **header_plist)
{
	(void)num_index;
    uint64_t *cpcon = &con_base[2*1]; /* +1:Reserved領域をスキップ */
    uint64_t *ctbl = &tbl_base[1];    /* +1:Reserved領域をスキップ */


    /* 前のテーブルの接続情報リストを介して連結可能なエントリが存在するか検索し、接続情報とテーブルをマージします */
    PgpMgrAnalyzeLBL *tmpl;
    for (tmpl = *header_plist; tmpl != NULL; tmpl = tmpl->next) {
        int i;
        /* 複数ある場合最も大きなIDをマージ先とする */
        int j = -1;
        for (i = 64-1; i >= 1; --i) {
            if (tmpl->pcon>>i & 0x1) {
                j = i - 1; /* -1:jはReserved領域補正済に対するインデックスであるため */
                break;
            }
        }
        ASSERT(j >= 0);
        if (!(j >= 0)) {
            return;
        }
                
        uint64_t ptblv = 0;
        pack_tbl(&ptblv, tmpl->minx, tmpl->miny, tmpl->maxx, tmpl->maxy, tmpl->area, tmpl->pflg);
        mrg_two_p(&ctbl[j], &ptblv);
        cpcon[2*j+0] |= tmpl->pcon;
    }
}

/* 2エントリのデータをマージします */
static void mrg_two(uint64_t data, int32_t *minx, int32_t *miny, int32_t *maxx, int32_t *maxy, int32_t *area, int32_t *pflg)
{
    int32_t tmp;

    tmp   = get_tbl(data, SEL_MINX);
    *minx = (*minx > tmp) ? tmp : *minx;

    tmp   = get_tbl(data, SEL_MINY);
    *miny = (*miny > tmp) ? tmp : *miny;

    tmp   = get_tbl(data, SEL_MAXX);
    *maxx = (*maxx < tmp) ? tmp : *maxx;

    tmp   = get_tbl(data, SEL_MAXY);
    *maxy = (*maxy < tmp) ? tmp : *maxy;

    *area = *area + get_tbl(data, SEL_AREA);
    *pflg = *pflg | get_tbl(data, SEL_PFLG);

}

/* 処理中のテーブルの全エントリをマージします */
static int32_t mrg_tbl(uint32_t t_cnt, int32_t num_index, uint64_t *ccon, uint64_t *pcon, uint32_t *vflg, uint64_t *tbl_base, PgpMgrAnalyzeLBL **header_flist, PgpMgrAnalyzeLBL **header_plist, PgpMgrAnalyzeLBL **header_clist, uint32_t *f_cnt)
{
	(void)header_plist;
    int32_t  minx, miny, maxx, maxy, area, pflg;
    int32_t  i, j;
    int32_t  tmp;
    uint64_t tmpc;

    for (i = num_index; i > 0; i--) {
        if (*(vflg + i - 1) == F_VAL) {
            tmpc = *(ccon + i - 1);
            minx = get_tbl(*(tbl_base + i) ,SEL_MINX);
            miny = get_tbl(*(tbl_base + i) ,SEL_MINY);
            maxx = get_tbl(*(tbl_base + i) ,SEL_MAXX);
            maxy = get_tbl(*(tbl_base + i) ,SEL_MAXY);
            area = get_tbl(*(tbl_base + i) ,SEL_AREA);
            pflg = get_tbl(*(tbl_base + i) ,SEL_PFLG);
            /* Current Connectionを判定し、マージします */
            for (j = 1; j < num_index + 1; j++) {
                if (j != i) {
                    tmp = get_con(tmpc, j);
                    if (tmp == 1) {
                        mrg_two(*(tbl_base + j), &minx, &miny, &maxx, &maxy, &area, &pflg);
                        *(pcon + i - 1) = *(pcon + i - 1) | *(pcon + j - 1);
                    }
                }
            }

            tmpc = *(pcon + i - 1);
            /* 前テーブルとの連結がないか、最後のテーブルであれば完了リストに、そうでなければ繰越リストに追加します */
            if ((tmpc == 0) || (t_cnt == 0)) {
                if (append_llist(header_flist, minx, miny, maxx, maxy, area, pflg, tmpc) == NULL) {
                    return -AG903_ENOMEM;
                }
                *f_cnt = *f_cnt + 1;
            } else {
                if (append_llist(header_clist, minx, miny, maxx, maxy, area, pflg, tmpc) == NULL) {
                    return -AG903_ENOMEM;
                }
            }
        }
    }
    return 0;
}

/* 入力テーブルをもとに全エントリのマージ処理を行います */
static int32_t mrg_all(uint32_t tbl_num, uint64_t *src, PgpMgrAnalyzeLBL **header_flist, uint32_t *f_cnt, int32_t *gflg)
{
    PgpMgrAnalyzeLBL *header_clist = NULL;
    PgpMgrAnalyzeLBL *header_plist = NULL;
    int32_t  err = 0;
    uint32_t t_cnt;
    uint64_t *tbl_base;
    uint64_t *con_base;
    int32_t  num_index;     /* number of index in the table */
    uint64_t *ccon;         /* work for current connection merge  */
    uint64_t *pcon;         /* work for previous connection merge */
    uint32_t *vflg;         /* work; valid flag of each entry     */

    t_cnt = tbl_num;
    *gflg = 0;
    /* マージ処理のワーク領域を確保します */
    ccon = AG903_PGP_COMMON_MALLOC(sizeof(uint64_t)*MAX_INDEX);
    pcon = AG903_PGP_COMMON_MALLOC(sizeof(uint64_t)*MAX_INDEX);
    vflg = AG903_PGP_COMMON_MALLOC(sizeof(uint32_t)*MAX_INDEX);
    if ((ccon == NULL) || (pcon == NULL) || (vflg == NULL)) {
        RELEASE(ccon);
        RELEASE(pcon);
        RELEASE(vflg);
        return -AG903_ENOMEM;
    }
    while (t_cnt > 0) {
        /* テーブル単位でマージ処理を行います */
        t_cnt = t_cnt - 1;
        /* 処理するテーブルのポインタを参照します */
        tbl_base = src + (t_cnt * (MAX_INDEX + 1) * INDEX_SIZE);
        /* Connectionの先頭を参照します */
        con_base = tbl_base + (MAX_INDEX + 1);
        *gflg = *gflg | get_tbl(*tbl_base ,SEL_GFLG);
        num_index = get_tbl(*tbl_base ,SEL_INDX);
        /* Indexは1〜MAX_INDEXの範囲しか出力されない仕様 */
        ASSERT(num_index <= MAX_INDEX);
        /* Indexが存在しないテーブルはスキップします（生成されるIndexの数がフラッシュの閾値の倍数の場合に起こりえる条件） */
        if (num_index != 0) {
            /* 前のテーブルを現在処理中であるテーブルのConnectionとテーブルを加工することでマージします */
            pcon_tbl(num_index, con_base, tbl_base, &header_plist);
            /* マージ判定の前処理として、処理中のテーブルのConnectionを加工します */
            con_tbl(num_index, ccon, pcon, vflg, con_base);
            /* 処理中のテーブルの全エントリをマージします */
            if ((err = mrg_tbl(t_cnt, num_index, ccon, pcon, vflg, tbl_base, header_flist, &header_plist, &header_clist, f_cnt)) != 0) {
                rm_llist(header_flist);
                rm_llist(&header_clist);
                rm_llist(&header_plist);
                goto RELEASE_WORK;
            }
            /* リストを更新します */
            rm_llist(&header_plist);
            header_plist = header_clist;
            header_clist = NULL;
        }
    }
RELEASE_WORK:
    /* 全テーブル処理後にワーク領域を解放します */
    AG903_PGP_COMMON_FREE(ccon);
    AG903_PGP_COMMON_FREE(pcon);
    AG903_PGP_COMMON_FREE(vflg);
    return err;
}

/* 1エントリのデータを出力します */
static void entry_out(PgpMgrAnalyzeLBL *tmpl, uint64_t *tbl_base, uint64_t *con_base, uint32_t o_cnt, uint64_t ccon)
{
    int32_t area, pflg;

        area  = tmpl->area;
        pflg  = tmpl->pflg;
        *(tbl_base + o_cnt) = (((uint64_t)pflg << SHFT_PFLG) & MASK_PFLG) |
                              (((uint64_t)area << SHFT_AREA) & MASK_AREA) |
                              (((uint64_t)(tmpl->maxy) << SHFT_MAXY) & MASK_MAXY) |
                              (((uint64_t)(tmpl->maxx) << SHFT_MAXX) & MASK_MAXX) |
                              (((uint64_t)(tmpl->miny) << SHFT_MINY) & MASK_MINY) |
                              (((uint64_t)(tmpl->minx) << SHFT_MINX) & MASK_MINX);
        *(con_base + o_cnt * 2    ) = ccon;
        *(con_base + o_cnt * 2 + 1) = tmpl->pcon;
}

/* テーブル内の空きエントリにゼロをパディングします */
static void pad_zero(uint64_t *tbl_base, uint64_t *con_base, uint32_t s_cnt)
{
    uint32_t i;

    for (i = s_cnt; i < MAX_INDEX + 1; i++) {
        *(tbl_base + i        ) = 0;
        *(con_base + i * 2    ) = 0;
        *(con_base + i * 2 + 1) = 0;
    }
}

/* テーブルヘッダを出力します */
static void hdr_out(uint64_t *tbl_base, uint64_t *con_base, int32_t gflg, int32_t tbl_port, int32_t tbl_frid, int32_t eline, int32_t sline, uint32_t o_cnt, int32_t tbl_id)
{
    *(tbl_base    ) = (((uint64_t)gflg     << SHFT_GFLG) & MASK_GFLG) |
                      (((uint64_t)tbl_port << SHFT_PORT) & MASK_PORT) |
                      (((uint64_t)tbl_frid << SHFT_FRID) & MASK_FRID) |
                      (((uint64_t)eline    << SHFT_ELIN) & MASK_ELIN) |
                      (((uint64_t)sline    << SHFT_SLIN) & MASK_SLIN) |
                      (((uint64_t)o_cnt    << SHFT_INDX) & MASK_INDX) |
                      (((uint64_t)tbl_id   << SHFT_ID  ) & MASK_ID  );
    *(con_base    ) = 0;
    *(con_base + 1) = 0;
}

/* マージ結果をもとに出力テーブルを生成します */
static int32_t gentbl_all(int32_t gflg, uint64_t *plist, uint64_t *src, uint32_t f_cnt, PgpMgrAnalyzeLBL **header_flist)
{
    int32_t  gflag, tbl_port, tbl_frid, tbl_id;
    int32_t  sline, eline;
    uint32_t r_cnt, o_cnt;
    uint64_t ccon;
    uint64_t *tbl_base, *con_base;
    PgpMgrAnalyzeLBL *tmpl;

    gflag = gflg & RES_END;
    tbl_port = get_tbl(*src, SEL_PORT);
    tbl_frid = get_tbl(*src, SEL_FRID);
    tbl_id   = get_tbl(*src, SEL_ID  );
    eline = MIN_LINE;
    sline = MAX_LINE;
    r_cnt = f_cnt;
    o_cnt = 0;
    ccon  = INIT_CCON;
    tbl_base = plist;
    con_base = plist + (MAX_INDEX + 1);
    for (tmpl = *header_flist; tmpl != NULL; tmpl = tmpl->next) {
        o_cnt = o_cnt + 1;
        entry_out(tmpl, tbl_base, con_base, o_cnt, ccon);
        sline = (sline > tmpl->miny) ? tmpl->miny : sline;
        eline = (eline < tmpl->maxy) ? tmpl->maxy : eline;
        /* 1テーブルの作成終了を判定し、ヘッダの出力を行います */
        if ((r_cnt == o_cnt) || (o_cnt == MAX_INDEX)) {
            if (r_cnt == o_cnt) {
                if (o_cnt < MAX_INDEX) {
                    pad_zero(tbl_base, con_base, o_cnt + 1);
                }
                r_cnt = 0;
                gflag = gflag | SET_END;
            } else {
                r_cnt = r_cnt - MAX_INDEX;
            }
            hdr_out(tbl_base, con_base, gflag, tbl_port, tbl_frid, eline, sline, o_cnt, tbl_id);
            eline = MIN_LINE;
            sline = MAX_LINE;
            o_cnt = 0;
            tbl_id = tbl_id + 1;
            ccon  = INIT_CCON; /* initial value of current connection */
            tbl_base = tbl_base + (MAX_INDEX + 1) * INDEX_SIZE;
            con_base = con_base + (MAX_INDEX + 1) * INDEX_SIZE;
        } else {
            ccon = ccon >> 1;
        }
    }
    rm_llist(header_flist);
    if (r_cnt != 0) {
        return 1;
    } else {
        return 0;
    }
}

/**
 * @brief   ラベリング付加情報テーブルの解析を行ない、解析結果を戻します。
 * @param   src [in] ラベリング付加情報テーブルが格納されたポインタを指定します。
 * @param   tbl_num [in] ラベリング付加情報テーブル数を指定します。
 * @param   out_tbl_num [out] 解析後のテーブル数
 * @return  解析結果を格納したポインタ
 * @note    解析結果を格納するメモリや、解析途中で使用するテンポラリ領域のメモリ確保に失敗した場合NULLが返ります。<p>
 *          本関数はユーザー側が直接使用することはありません。
*/
void *PgpMgrAnalyzeLabelingTable(void *src, uint32_t tbl_num, uint32_t *out_tbl_num)
{
    PgpMgrAnalyzeLBL *header_flist = NULL;
    int32_t  gflg = 0;
    uint32_t f_cnt = 0;
    uint64_t *plist = NULL;

    /* 入力テーブルをもとに全エントリのマージ処理を行います */
    if (mrg_all(tbl_num, (uint64_t *)src, &header_flist, &f_cnt, &gflg) != 0) {
        return NULL;
    }

    if (out_tbl_num != NULL) {
        *out_tbl_num = (f_cnt + MAX_INDEX - 1) / MAX_INDEX;
    }

    /* 出力テーブルのメモリを確保します */
    if ((plist = AG903_PGP_COMMON_MALLOC(sizeof(uint64_t) * (MAX_INDEX + 1) * INDEX_SIZE * ((f_cnt + MAX_INDEX - 1) / MAX_INDEX))) == NULL) {
        rm_llist(&header_flist);
        return NULL;
    }
    /* マージ済みのテーブル生成を行います */
    if (gentbl_all(gflg, plist, (uint64_t *)src, f_cnt, &header_flist) != 0) {
        return NULL;
    }

    return plist;
}

/**
 * @brief   ラベリング付加情報テーブルを開放します。
 * @param   tbl [in] 解析結果のポインタ
 * @return  実行結果
 * @note    この関数は PgpMgrAnalyzeLabelingTableと対で使用します。<p>
 *          本関数はユーザー側が直接使用することはありません。
*/
void PgpMgrReleaseLabelingTable(void *tbl)
{
    AG903_PGP_COMMON_FREE(tbl);
}
