C語言編程技巧——表驅動法

李肖遙 2024-06-25 22:10
? ? 關注、星標公眾號,直達精彩內容


1 概念提出

所謂表驅動法(Table-Driven Approach)簡而言之就是用查表的方法獲取數據。此處的“表”通常為數組,但可視為數據庫的一種體現。

根據字典中的部首檢字表查找讀音未知的漢字就是典型的表驅動法,即以每個字的字形為依據,計算出一個索引值,并映射到對應的頁數。相比一頁一頁地順序翻字典查字,部首檢字法效率極高。

具體到編程方面,在數據不多時可用邏輯判斷語句(if…else或switch…case)來獲取值;但隨著數據的增多,邏輯語句會越來越長,此時表驅動法的優勢就開始顯現。

例如,用36進制(A表示10,B表示11,…)表示更大的數字,邏輯判斷語句如下:

if(ucNum < 10)
{
ucNumChar = ConvertToChar(ucNum);
}
elseif(ucNum == 10)
{
ucNumChar = 'A';
}
elseif(ucNum == 11)
{
ucNumChar = 'B';
}
elseif(ucNum == 12)
{
ucNumChar = 'C';
}
//... ...
elseif(ucNum == 35)
{
ucNumChar = 'Z';
}

當然也可以用switch…case結構,但實現都很冗長。而用表驅動法(將numChar存入數組)則非常直觀和簡潔。如:

CHAR aNumChars[] = {'0', '1', '2', /*3~9*/'A', 'B', 'C', /*D~Y*/'Z'};
CHAR ucNumChar = aNumChars[ucNum % sizeof(aNumChars)];

像這樣直接將變量當作下數組下標來讀取數值的方法就是直接查表法。

注意,如果熟悉字符串操作,則上述寫法可以更簡潔:

CHAR ucNumChar = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[ucNum];

使用表驅動法時需要關注兩個問題:一是如何查表,從表中讀取正確的數據;二是表里存放什么,如數值或函數指針。前者參見1.1節“查表方式”內容,后者參見1.2節“實戰示例”內容。

1.1 查表方式

常用的查表方式有直接查找、索引查找和分段查找等。

1.1.1 直接查找

即直接通過數組下標獲取到數據。如果熟悉哈希表的話,可以很容易看出這種查表方式就是哈希表的直接訪問法。

如獲取星期名稱,邏輯判斷語句如下:

if(0 == ucDay)
{
pszDayName = "Sunday";
}
elseif(1 == ucDay)
{
pszDayName = "Monday";
}
//... ...
elseif(6 == ucDay)
{
pszDayName = "Saturday";
}

而實現同樣的功能,可將這些數據存儲到一個表里:

CHAR *paNumChars[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",  "Saturday"};
CHAR *pszDayName = paNumChars[ucDay];

類似哈希表特性,表驅動法適用于無需有序遍歷數據,且數據量大小可提前預測的情況。

對于過于復雜和龐大的判斷,可將數據存為文件,需要時加載文件初始化數組,從而在不修改程序的情況下調整里面的數值。

有時,訪問之前需要先進行一次鍵值轉換。如表驅動法表示端口忙閑時,需將槽位端口號映射為全局編號。所生成的端口數目大小的數組,其下標對應全局端口編號,元素值表示相應端口的忙閑狀態。

1.1.2 索引查找

有時通過一次鍵值轉換,依然無法把數據(如英文單詞等)轉為鍵值。此時可將轉換的對應關系寫到一個索引表里,即索引訪問。

如現有100件商品,4位編號,范圍從0000到9999。此時只需要申請一個長度為100的數組,且對應2位鍵值。但將4位的編號轉換為2位的鍵值,可能過于復雜或沒有規律,最合適的方法是建立一個保存該轉換關系的索引表。采用索引訪問既節省內存,又方便維護。比如索引A表示通過名稱訪問,索引B表示通過編號訪問。

1.1.3 分段查找

通過確定數據所處的范圍確定分類(下標)。有的數據可分成若干區間,即具有階梯性,如分數等級。此時可將每個區間的上限(或下限)存到一個表中,將對應的值存到另一表中,通過第一個表確定所處的區段,再由區段下標在第二個表里讀取相應數值。注意要留意端點,可用二分法查找,另外可考慮通過索引方法來代替。

如根據分數查績效等級:

#define MAX_GRADE_LEVEL   (INT8U)5
DOUBLE aRangeLimit[MAX_GRADE_LEVEL] = {50.0, 60.0, 70.0, 80.0, 100.0};
CHAR *paGrades[MAX_GRADE_LEVEL] = {"Fail", "Pass", "Credit", "Distinction", "High Distinction"};

static CHAR* EvaluateGrade(DOUBLE dScore)
{
INT8U ucLevel = 0;
for(; ucLevel < MAX_GRADE_LEVEL; ucLevel++)
{
if(dScore < aRangeLimit[ucLevel])
return paGrades[ucLevel];
}
return paGrades[0];
}

上述兩張表(數組)也可合并為一張表(結構體數組),如下所示:

typedefstruct{
DOUBLE aRangeLimit;
CHAR *pszGrade;
}T_GRADE_MAP;

T_GRADE_MAP gGradeMap[MAX_GRADE_LEVEL] = {
{50.0, "Fail"},
{60.0, "Pass"},
{70.0, "Credit"},
{80.0, "Distinction"},
{100.0, "High Distinction"}
};

static CHAR* EvaluateGrade(DOUBLE dScore)
{
INT8U ucLevel = 0;
for(; ucLevel < MAX_GRADE_LEVEL; ucLevel++)
{
if(dScore < gGradeMap[ucLevel].aRangeLimit)
return gGradeMap[ucLevel].pszGrade;
}
return gGradeMap[0].pszGrade;
}

該表結構已具備的數據庫的雛形,并可擴展支持更為復雜的數據。其查表方式通常為索引查找,偶爾也為分段查找;當索引具有規律性(如連續整數)時,退化為直接查找。

使用分段查找法時應注意邊界,將每一分段范圍的上界值都考慮在內。找出所有不在最高一級范圍內的值,然后把剩下的值全部歸入最高一級中。有時需要人為地為最高一級范圍添加一個上界。

同時應小心不要錯誤地用“<”來代替“<=”。要保證循環在找出屬于最高一級范圍內的值后恰當地結束,同時也要保證恰當處理范圍邊界。

1.2 實戰示例

本節多數示例取自實際項目。表形式為一維數組、二維數組和結構體數組;表內容有數據、字符串和函數指針。基于表驅動的思想,表形式和表內容可衍生出豐富的組合。

1.2.1 字符統計

問題:統計用戶輸入的一串數字中每個數字出現的次數。

普通解法主體代碼如下:

INT32U aDigitCharNum[10] = {0}; /* 輸入字符串中各數字字符出現的次數 */
INT32U dwStrLen = strlen(szDigits);

INT32U dwStrIdx = 0;
for(; dwStrIdx < dwStrLen; dwStrIdx++)
{
switch(szDigits[dwStrIdx])
{
case'1':
aDigitCharNum[0]++;
break;
case'2':
aDigitCharNum[1]++;
break;
//... ...
case'9':
aDigitCharNum[8]++;
break;
}
}

這種解法的缺點顯而易見,既不美觀也不靈活。其問題關鍵在于未將數字字符與數組aDigitCharNum下標直接關聯起來。

以下示出更簡潔的實現方式:

for(; dwStrIdx < dwStrLen; dwStrIdx++)
{
aDigitCharNum[szDigits[dwStrIdx] - '0']++;
}

上述實現考慮到0也為數字字符。該解法也可擴展至統計所有ASCII可見字符。

1.2.2 月天校驗

問題:對給定年份和月份的天數進行校驗(需區分平年和閏年)。

普通解法主體代碼如下:

switch(OnuTime.Month)
{
case1:
case3:
case5:
case7:
case8:
case10:
case12:
if(OnuTime.Day>31 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~31)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
break;
case2:
if(((OnuTime.Year%4 == 0) && (OnuTime.Year%100 != 0)) || (OnuTime.Year%400 == 0))
{
if(OnuTime.Day>29 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~29)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
}
else
{
if(OnuTime.Day>28 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~28)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
}
break;
case4:
case6:
case9:
case11:
if(OnuTime.Day>30 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~30)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
break;
default:
CtcOamLog(FUNCTION_Pon,"Don't support this Month: %d(1~12)!!!\n", OnuTime.Month);
retcode = S_ERROR;
break;
}

以下示出更簡潔的實現方式:

#define MONTH_OF_YEAR 12    /* 一年中的月份數 */

/* 閏年:能被4整除且不能被100整除,或能被400整除 */
#define IS_LEAP_YEAR(year) ((((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0))

/* 平年中的各月天數,下標對應月份 */
INT8U aDayOfCommonMonth[MONTH_OF_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

INT8U ucMaxDay = 0;
if((OnuTime.Month == 2) && (IS_LEAP_YEAR(OnuTime.Year)))
ucMaxDay = aDayOfCommonMonth[1] + 1;
else
ucMaxDay = aDayOfCommonMonth[OnuTime.Month-1];

if((OnuTime.Day < 1) || (OnuTime.Day > ucMaxDay)
{
CtcOamLog(FUNCTION_Pon,"Month %d doesn't have this Day: %d(1~%d)!!!\n",
OnuTime.Month, OnuTime.Day, ucMaxDay);
retcode = S_ERROR;
}

1.2.3 名稱構造

問題:根據WAN接口承載的業務類型(Bitmap)構造業務類型名稱字符串。

普通解法主體代碼如下:

void Sub_SetServerType(INT8U *ServerType, INT16U wan_servertype)
{
if ((wan_servertype & 0x0001) == 0x0001)
{
strcat(ServerType, "_INTERNET");
}
if ((wan_servertype & 0x0002) == 0x0002)
{
strcat(ServerType, "_TR069");
}
if ((wan_servertype & 0x0004) == 0x0004)
{
strcat(ServerType, "_VOIP");
}
if ((wan_servertype & 0x0008) == 0x0008)
{
strcat(ServerType, "_OTHER");
}
}

以下示出C語言中更簡潔的實現方式:

#define  GET_BIT(var, bit)   (((var) >> (bit)) & 0x1)    /* 獲取var變量第bit位,編號從右至左 */
const CHAR* paSvrNames[] = {"_INTERNET", "_TR069", "_VOIP", "_OTHER"};
const INT8U ucSvrNameNum = sizeof(paSvrNames) / sizeof(paSvrNames[0]);

VOID SetServerType(CHAR *pszSvrType, INT16U wSvrType)
{
INT8U ucIdx = 0;
for(; ucIdx < ucSvrNameNum; ucIdx++)
{
if(1 == GET_BIT(wSvrType, ucIdx))
strcat(pszSvrType, paSvrNames[ucIdx]);
}
}

新的實現將數據和邏輯分離,維護起來非常方便。只要邏輯(規則)不變,則唯一可能的改動就是數據(paSvrNames)。

1.2.4 值名解析

問題:根據枚舉變量取值輸出其對應的字符串,如PORT_FE(1)輸出“Fe”。

//值名映射表結構體定義,用于數值解析器
typedefstruct{
INT32U dwElem; //待解析數值,通常為枚舉變量
CHAR* pszName; //指向數值所對應解析名字符串的指針
}T_NAME_PARSER;

/******************************************************************************
* 函數名稱: NameParser
* 功能說明: 數值解析器,將給定數值轉換為對應的具名字符串
* 輸入參數: VOID *pvMap :值名映射表數組,含T_NAME_PARSER結構體類型元素
VOID指針允許用戶在保持成員數目和類型不變的前提下,
定制更有意義的結構體名和/或成員名。
INT32U dwEntryNum :值名映射表數組條目數
INT32U dwElem :待解析數值,通常為枚舉變量
INT8U* pszDefName :缺省具名字符串指針,可為空
* 輸出參數: NA
* 返回值 : INT8U *: 數值所對應的具名字符串
當無法解析給定數值時,若pszDefName為空,則返回數值對應的16進制格式
字符串;否則返回pszDefName。
******************************************************************************/

INT8U *NameParser(VOID *pvMap, INT32U dwEntryNum, INT32U dwElem, INT8U* pszDefName)
{
CHECK_SINGLE_POINTER(pvMap, "NullPoniter");

INT32U dwEntryIdx = 0;
for(dwEntryIdx = 0; dwEntryIdx < dwEntryNum; dwEntryIdx++)
{
T_NAME_PARSER *ptNameParser = (T_NAME_PARSER *)pvMap;
if(dwElem == ptNameParser->dwElem)
{
return ptNameParser->pszName;
}
//ANSI標準禁止對void指針進行算法操作;GNU標準則指定void*算法操作與char*一致。
//若考慮移植性,可將pvMap類型改為INT8U*,或定義INT8U*局部變量指向pvMap。
pvMap += sizeof(T_NAME_PARSER);
}

if(NULL != pszDefName)
{
return pszDefName;
}
else
{
static INT8U szName[12] = {0}; //Max:"0xFFFFFFFF"
sprintf(szName, "0x%X", dwElem);
return szName;
}
}

以下給出NameParser的簡單應用示例:

//UNI端口類型值名映射表結構體定義
typedefstruct{
INT32U dwPortType;
INT8U* pszPortName;
}T_PORT_NAME;
//UNI端口類型解析器
T_PORT_NAME gUniNameMap[] = {
{1, "Fe"},
{3, "Pots"},
{99, "Vuni"}
};
const INT32U UNI_NAM_MAP_NUM = (INT32U)(sizeof(gUniNameMap)/sizeof(T_PORT_NAME));
VOID NameParserTest(VOID)
{
INT8U ucTestIndex = 1;

printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Unknown", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 0, "Unknown")) ? "ERROR" : "OK");
printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("DefName", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 0, "DefName")) ? "ERROR" : "OK");
printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Fe", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 1, "Unknown")) ? "ERROR" : "OK");
printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Pots", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 3, "Unknown")) ? "ERROR" : "OK");
printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Vuni", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 99, NULL)) ? "ERROR" : "OK");
printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Unknown", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 255, "Unknown")) ? "ERROR" : "OK");
printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("0xABCD", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 0xABCD, NULL)) ? "ERROR" : "OK");
printf("[%s] Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("NullPoniter", NameParser(NULL, UNI_NAM_MAP_NUM, 0xABCD, NULL)) ? "ERROR" : "OK");
}

gUniNameMap在實際項目中有十余個條目,若采用邏輯鏈實現將非常冗長。

1.2.5 取值映射

問題:不同模塊間同一參數枚舉值取值可能有所差異,需要適配。

此處不再給出普通的switch…case或if…else if…else結構,而直接示出以下表驅動實現:

typedefstruct{
PORTSTATE loopMEState;
PORTSTATE loopMIBState;
}LOOPMAPSTRUCT;

static LOOPMAPSTRUCT s_CesLoop[] = {
{NO_LOOP, e_ds1_looptype_noloop},
{PAYLOAD_LOOP, e_ds1_looptype_PayloadLoop},
{LINE_LOOP, e_ds1_looptype_LineLoop},
{PON_LOOP, e_ds1_looptype_OtherLoop},
{CES_LOOP, e_ds1_looptype_InwardLoop}};

PORTSTATE ConvertLoopMEStateToMIBState(PORTSTATE vPortState)
{
INT32U num = 0, ii;

num = ARRAY_NUM(s_CesLoop);
for(ii = 0; ii < num; ii++)
{
if(vPortState == s_CesLoop[ii].loopMEState)
return s_CesLoop[ii].loopMIBState;
}
return e_ds1_looptype_noloop;
}

相應地,從loopMIBState映射到loopMEState需要定義一個ConvertLoopMIBStateToMEState函數。更進一步,所有類似的一對一映射關系都必須如上的映射(轉換)函數,相當繁瑣。

事實上,從抽象層面看,該映射關系非常簡單。提取共性后定義帶參數宏,如下所示:

/**********************************************************
* 功能描述:進行二維數組映射表的一對一映射,用于參數適配
* 參數說明:map -- 二維數組映射表
elemSrc -- 映射源,即待映射的元素值
elemDest -- 映射源對應的映射結果
direction -- 映射方向字節,表示從數組哪列映射至哪列。
高4位對應映射源列,低4位對應映射結果列。
defaultVal -- 映射失敗時置映射結果為缺省值
* 示例: ARRAY_MAPPER(gCesLoopMap, 3, ucLoop, 0x10, NO_LOOP);
則ucLoop = 2(LINE_LOOP)
**********************************************************/

#define ARRAY_MAPPER(map, elemSrc, elemDest, direction, defaultVal) do{\
INT8U ucMapIdx = 0, ucMapNum = 0; \
ucMapNum = sizeof(map)/sizeof(map[0]); \
for(ucMapIdx = 0; ucMapIdx < ucMapNum; ucMapIdx++) \
{ \
if((elemSrc) == map[ucMapIdx][((direction)&0xF0)>>4]) \
{ \
elemDest = map[ucMapIdx][(direction)&0x0F]; \
break; \
} \
} \
if(ucMapIdx == ucMapNum) \
{ \
elemDest = (defaultVal); \
} \
}while(0)

參數取值轉換時直接調用統一的映射器宏,如下:

static INT8U gCesLoopMap[][2] = {
{NO_LOOP, e_ds1_looptype_noloop},
{PAYLOAD_LOOP, e_ds1_looptype_PayloadLoop},
{LINE_LOOP, e_ds1_looptype_LineLoop},
{PON_LOOP, e_ds1_looptype_OtherLoop},
{CES_LOOP, e_ds1_looptype_InwardLoop}};

ARRAY_MAPPER(gCesLoopMap, tPara.dwParaVal[0], dwLoopConf, 0x01, e_ds1_looptype_noloop);

另舉一例:

#define  CES_DEFAULT_JITTERBUF        (INT32U)2000   /* 默認jitterbuf為2000us,而1幀=125us */
#define CES_JITTERBUF_STEP (INT32U)125 /* jitterbuf步長為125us,即1幀 */
#define CES_DEFAULT_QUEUESIZE (INT32U)5
#define CES_DEFAULT_MAX_QUEUESIZE (INT32U)7

#define ARRAY_NUM(array) (sizeof(array) / sizeof((array)[0])) /* 數組元素個數 */
typedefstruct{
INT32U dwJitterBuffer;
INT32U dwFramePerPkt;
INT32U dwQueueSize;
}QUEUE_SIZE_MAP;
/* gCesQueueSizeMap也可以(JitterBuffer / FramePerPkt)值為索引,更加緊湊 */
static QUEUE_SIZE_MAP gCesQueueSizeMap[]= {
{1,1,1}, {1,2,1}, {2,1,2}, {2,2,1},
{3,1,3}, {3,2,1}, {4,1,3}, {4,2,1},
{5,1,4}, {5,2,3}, {6,1,4}, {6,2,3},
{7,1,4}, {7,2,3}, {8,1,4}, {8,2,3},
{9,1,5}, {9,2,4}, {10,1,5}, {10,2,4},
{11,1,5}, {11,2,4}, {12,1,5}, {12,2,4},
{13,1,5}, {13,2,4}, {14,1,5}, {14,2,4},
{15,1,5}, {15,2,4}, {16,1,5}, {16,2,4},
{17,1,6}, {17,2,5}, {18,1,6}, {18,2,5},
{19,1,6}, {19,2,5}, {20,1,6}, {20,2,5},
{21,1,6}, {21,2,5}, {22,1,6}, {22,2,5},
{23,1,6}, {23,2,5}, {24,1,6}, {24,2,5},
{25,1,6}, {25,2,5}, {26,1,6}, {26,2,5},
{27,1,6}, {27,2,5}, {28,1,6}, {28,2,5},
{29,1,6}, {29,2,5}, {30,1,6}, {30,2,5},
{31,1,6}, {31,2,5}, {32,1,6}, {32,2,5}};
/**********************************************************
* 函數名稱: CalcQueueSize
* 功能描述: 根據JitterBuffer和FramePerPkt計算QueueSize
* 注意事項: 配置的最大緩存深度
* = 2 * JitterBuffer / FramePerPkt
* = 2 * N Packet = 2 ^ QueueSize
* JitterBuffer為125us幀速率的倍數,
* FramePerPkt為每個分組的幀數,
* QueueSize向上取整,最大為7。
**********************************************************/

INT32U CalcQueueSize(INT32U dwJitterBuffer, INT32U dwFramePerPkt)
{
INT8U ucIdx = 0, ucNum = 0;

//本函數暫時僅考慮E1
ucNum = ARRAY_NUM(gCesQueueSizeMap);
for(ucIdx = 0; ucIdx < ucNum; ucIdx++)
{
if((dwJitterBuffer == gCesQueueSizeMap[ucIdx].dwJitterBuffer) &&
(dwFramePerPkt == gCesQueueSizeMap[ucIdx].dwFramePerPkt))
{
return gCesQueueSizeMap[ucIdx].dwQueueSize;
}
}

return CES_DEFAULT_MAX_QUEUESIZE;
}

1.2.6 版本控制

問題:控制OLT與ONU之間的版本協商。ONU本地設置三比特控制字,其中bit2(MSB)~bit0(LSB)分別對應0x21、0x30和0xAA版本號;且bitX為0表示上報對應版本號,bitX為1表示不上報對應版本號。其他版本號如0x20、0x13和0x1必須上報,即不受控制。

最初的實現采用if…else if…else結構,代碼非常冗長,如下:

pstSendTlv->ucLength = 0x1f;
if (gOamCtrlCode == 0)
{
vosMemCpy(pstSendTlv->aucVersionList, ctc_oui, 3);
pstSendTlv->aucVersionList[3] = 0x30;
vosMemCpy(&(pstSendTlv->aucVersionList[4]), ctc_oui, 3);
pstSendTlv->aucVersionList[7] = 0x21;
vosMemCpy(&(pstSendTlv->aucVersionList[8]), ctc_oui, 3);
pstSendTlv->aucVersionList[11] = 0x20;
vosMemCpy(&(pstSendTlv->aucVersionList[12]), ctc_oui, 3);
pstSendTlv->aucVersionList[15] = 0x13;
vosMemCpy(&(pstSendTlv->aucVersionList[16]), ctc_oui, 3);
pstSendTlv->aucVersionList[19] = 0x01;
vosMemCpy(&(pstSendTlv->aucVersionList[20]), ctc_oui, 3);
pstSendTlv->aucVersionList[23] = 0xaa;
}
elseif (gOamCtrlCode == 1)
{
vosMemCpy(pstSendTlv->aucVersionList, ctc_oui, 3);
pstSendTlv->aucVersionList[3] = 0x30;
vosMemCpy(&(pstSendTlv->aucVersionList[4]), ctc_oui, 3);
pstSendTlv->aucVersionList[7] = 0x21;
vosMemCpy(&(pstSendTlv->aucVersionList[8]), ctc_oui, 3);
pstSendTlv->aucVersionList[11] = 0x20;
vosMemCpy(&(pstSendTlv->aucVersionList[12]), ctc_oui, 3);
pstSendTlv->aucVersionList[15] = 0x13;
vosMemCpy(&(pstSendTlv->aucVersionList[16]), ctc_oui, 3);
pstSendTlv->aucVersionList[19] = 0x01;
}
//此處省略gOamCtrlCode == 2~6的處理代碼
elseif (gOamCtrlCode == 7)
{
vosMemCpy(&(pstSendTlv->aucVersionList), ctc_oui, 3);
pstSendTlv->aucVersionList[3] = 0x20;
vosMemCpy(&(pstSendTlv->aucVersionList[4]), ctc_oui, 3);
pstSendTlv->aucVersionList[7] = 0x13;
vosMemCpy(&(pstSendTlv->aucVersionList[8]), ctc_oui, 3);
pstSendTlv->aucVersionList[11] = 0x01;
}

以下示出C語言中更簡潔的實現方式(基于二維數組):

/**********************************************************************
* 版本控制字數組定義
* gOamCtrlCode: Bitmap控制字。Bit-X為0時上報對應版本,Bit-X為1時屏蔽對應版本。
* CTRL_VERS_NUM: 可控版本個數。
* CTRL_CODE_NUM: 控制字個數。與CTRL_VERS_NUM有關。
* gOamVerCtrlMap: 版本控制字數組。行對應控制字,列對應可控版本。
元素值為0時不上報對應版本,元素值非0時上報該元素值。
* Note: 該數組旨在實現“數據與控制隔離”。后續若要新增可控版本,只需修改
-- CTRL_VERS_NUM
-- gOamVerCtrlMap新增行(控制字)
-- gOamVerCtrlMap新增列(可控版本)
**********************************************************************/

#define CTRL_VERS_NUM 3
#define CTRL_CODE_NUM (1<
u8_t gOamVerCtrlMap[CTRL_CODE_NUM][CTRL_VERS_NUM] = {
/* Ver21 Ver30 VerAA */
{0x21, 0x30, 0xaa}, /*gOamCtrlCode = 0*/
{0x21, 0x30, 0 }, /*gOamCtrlCode = 1*/
{0x21, 0, 0xaa}, /*gOamCtrlCode = 2*/
{0x21, 0, 0 }, /*gOamCtrlCode = 3*/
{ 0, 0x30, 0xaa}, /*gOamCtrlCode = 4*/
{ 0, 0x30, 0 }, /*gOamCtrlCode = 5*/
{ 0, 0, 0xaa}, /*gOamCtrlCode = 6*/
{ 0, 0, 0 } /*gOamCtrlCode = 7*/
};
#define INFO_TYPE_VERS_LEN 7 /* InfoType + Length + OUI + ExtSupport + Version */

u8_t verIdx = 0;
u8_t index = 0;
for(verIdx = 0; verIdx < CTRL_VERS_NUM; verIdx++)
{
if(gOamVerCtrlMap[gOamCtrlCode][verIdx] != 0)
{
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = gOamVerCtrlMap[gOamCtrlCode][verIdx];
}
}
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = 0x20;
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = 0x13;
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = 0x01;

pstSendTlv->ucLength = INFO_TYPE_VERS_LEN + index;

1.2.7 消息處理

問題:終端輸入不同的打印命令,調用相應的打印函數,以控制不同級別的打印。

這是一段消息(事件)驅動程序。本模塊接收其他模塊(如串口驅動)發送的消息,根據消息中的打印級別字符串和開關模式,調用不同函數進行處理。常見的實現方法如下:

void logall(void)
{
g_log_control[0] = 0xFFFFFFFF;
}

void noanylog(void)
{
g_log_control[0] = 0;
}

void logOam(void)
{
g_log_control[0] |= (0x01 << FUNCTION_Oam);
}
void nologOam(void)
{
g_log_control[0] &= ~(0x01 << FUNCTION_Oam);
}
//... ...
void logExec(char *name, INT8U enable)
{
CtcOamLog(FUNCTION_Oam,"log %s %d\n",name,enable);
if (enable == 1) /*log*/
{
if (strcasecmp(name,"all") == 0) { /*字符串比較,不區分大小寫*/
logall();
} elseif (strcasecmp(name,"oam") == 0) {
logOam();
} elseif (strcasecmp(name,"pon") == 0) {
logPon();
//... ...
} elseif (strcasecmp(name,"version") == 0) {
logVersion();
}
elseif (enable == 0) /*nolog*/
{
if (strcasecmp(name,"all") == 0) {
noanylog();
} elseif (strcasecmp(name,"oam") == 0) {
nologOam();
} elseif (strcasecmp(name,"pon") == 0) {
nologPon();
//... ...
} elseif (strcasecmp(name,"version") == 0) {
nologVersion();
}
else
{
printf("bad log para\n");
}
}

以下示出C語言中更簡潔的實現方式:

typedefstruct{
OAM_LOG_OFF = (INT8U)0,
OAM_LOG_ON = (INT8U)1
}E_OAM_LOG_MODE;
typedef FUNC_STATUS (*OamLogHandler)(VOID);
typedefstruct{
CHAR *pszLogCls; /* 打印級別 */
E_OAM_LOG_MODE eLogMode; /* 打印模式 */
OamLogHandler fnLogHandler; /* 打印函數 */
}T_OAM_LOG_MAP;

T_OAM_LOG_MAP gOamLogMap[] = {
{"all", OAM_LOG_OFF, noanylog},
{"oam", OAM_LOG_OFF, nologOam},
//... ...
{"version", OAM_LOG_OFF, nologVersion},

{"all", OAM_LOG_ON, logall},
{"oam", OAM_LOG_ON, logOam},
//... ...
{"version", OAM_LOG_ON, logVersion}
};
INT32U gOamLogMapNum = sizeof(gOamLogMap) / sizeof(T_OAM_LOG_MAP);

VOID logExec(CHAR *pszName, INT8U ucSwitch)
{
INT8U ucIdx = 0;
for(; ucIdx < gOamLogMapNum; ucIdx++)
{
if((ucSwitch == gOamLogMap[ucIdx].eLogMode) &&
(!strcasecmp(pszName, gOamLogMap[ucIdx].pszLogCls));
{
gOamLogMap[ucIdx].fnLogHandler();
return;
}
}
if(ucIdx == gOamLogMapNum)
{
printf("Unknown LogClass(%s) or LogMode(%d)!\n", pszName, ucSwitch);
return;
}
}

這種表驅動消息處理實現的優點如下:

  • 增強可讀性,消息如何處理從表中一目了然。

  • 增強可擴展性。更容易修改,要增加新的消息,只要修改數據即可,不需要修改流程。

  • 降低復雜度。通過把程序邏輯的復雜度轉移到人類更容易處理的數據中來,從而達到控制復雜度的目標。

  • 主干清晰,代碼重用。

    若各索引為順序枚舉值,則建立多維數組(每維對應一個索引),根據下標直接定位到處理函數,效率會更高。

    注意,考慮到本節實例中logOam/logPon或nologOam/nologPon等函數本質上是基于打印級別的比特操作,因此可進一步簡化。以下例舉其相似實現:

/* 日志控制類型定義 */
typedefenum
{
LOG_NORM = 0, /* 未分類日志,可用于通用日志 */
LOG_FRM, /* Frame,OMCI幀日志 */
LOG_PON, /* Pon,光鏈路相關日志 */
LOG_ETH, /* Ethernet,Layer2以太網日志 */
LOG_NET, /* Internet,Layer3網絡日志 */
LOG_MULT, /* Multicast,組播日志 */
LOG_QOS, /* QOS,流量日志 */
LOG_CES, /* Ces,TDM電路仿真日志 */
LOG_VOIP, /* Voip,語音日志 */
LOG_ALM, /* Alarm,告警日志 */
LOG_PERF, /* Performance,性能統計日志 */
LOG_VER, /* Version,軟件升級日志 */
LOG_XDSL, /* xDsl日志 */
LOG_DB, /* 數據庫操作日志 */
//新日志類型在此處擴展,共支持32種日志類型
LOG_ALL = UINT_MAX /* 所有日志類型 */
}E_LOG_TYPE;

/*****************************************************************************
* 變量名稱:gOmciLogCtrl
* 作用描述:OMCI日志控制字,BitMap格式(比特編號從LSB至MSB依次為Bit0->BitN)。
* Bit0~N分別對應E_LOG_TYPE各枚舉值(除LOG_ALL外)。
* BitX為0時關閉日志類型對應的日志功能,BitX為1時則予以打開。
* 變量范圍:該變量為四字節整型靜態全局變量,即支持32種日志類型。
* 訪問說明:通過GetOmciLogCtrl/SetOmciLogCtrl/OmciLogCtrl函數訪問/設置控制字。
*****************************************************************************/

static INT32U gOmciLogCtrl = 0;

//日志類型字符串數組,下標為各字符串所對應的日志類型枚舉值。
staticconst INT8U* paLogTypeName[] = {
"Norm", "Frame", "Pon", "Ethernet", "Internet",
"Multicast", "Qos", "Ces", "Voip", "Alarm",
"Performance", "Version", "Xdsl", "Db"
};
staticconst INT8U ucLogTypeNameNum = sizeof(paLogTypeName) / sizeof(paLogTypeName[0]);

static VOID SetGlobalLogCtrl(E_LOG_TYPE eLogType, INT8U ucLogSwitch)
{
if(LOG_ON == ucLogSwitch)
gOmciLogCtrl = LOG_ALL;
else
gOmciLogCtrl = 0;
}
static VOID SetSpecificLogCtrl(E_LOG_TYPE eLogType, INT8U ucLogSwitch)
{
if(LOG_ON == ucLogSwitch)
SET_BIT(gOmciLogCtrl, eLogType);
else
CLR_BIT(gOmciLogCtrl, eLogType);
}

VOID OmciLogCtrl(CHAR *pszLogType, INT8U ucLogSwitch)
{
if(0 == strncasecmp(pszLogType, "All", LOG_TYPE_CMP_LEN))
{
SetGlobalLogCtrl(LOG_ALL, ucLogSwitch);
return;
}

INT8U ucIdx = 0;
for(ucIdx = 0; ucIdx < ucLogTypeNameNum; ucIdx++)
{
if(0 == strncasecmp(pszLogType, paLogTypeName[ucIdx], LOG_TYPE_CMP_LEN))
{
SetSpecificLogCtrl(ucIdx, ucLogSwitch);
printf("LogType: %s, LogSwitch: %s\n", paLogTypeName[ucIdx],
(1==ucLogSwitch)?"On":"Off");
return;
}
}

OmciLogHelp();
}

2 編程思想

表驅動法屬于數據驅動編程的一種,其核心思想在《Unix編程藝術》和《代碼大全2》中均有闡述。兩者均認為人類閱讀復雜數據結構遠比復雜的控制流程容易,即相對于程序邏輯,人類更擅長于處理數據。

本節將由Unix設計原則中的“分離原則”和“表示原則”展開。

2.1 分離原則:策略同機制分離,接口同引擎分離

機制即提供的功能;策略即如何使用功能。

策略的變化要遠遠快于機制的變化。將兩者分離,可以使機制相對保持穩定,而同時支持策略的變化。

代碼大全中提到“隔離變化”的概念,以及設計模式中提到的將易變化的部分和不易變化的部分分離也是這個思路。

2.2 表示原則:把知識疊入數據以求邏輯質樸而健壯

即使最簡單的程序邏輯讓人類來驗證也很困難,但就算是很復雜的數據,對人類來說,還是相對容易推導和建模的。數據比編程邏輯更容易駕馭。在復雜數據和復雜代碼中選擇,寧可選擇前者。更進一步,在設計中,應該主動將代碼的復雜度轉移到數據中去(參考“版本控制”)。

在“消息處理”示例中,每個消息處理的邏輯不變,但消息可能是變化的。將容易變化的消息和不容易變化的查找邏輯分離,即“隔離變化”。此外,該例也體現消息內部的處理邏輯(機制)和不同的消息處理(策略)分離。

數據驅動編程可以應用于:

  • 函數級設計,如本文示例。

  • 程序級設計,如用表驅動法實現狀態機。

  • 系統級設計,如DSL。

注意,數據驅動編程不是全新的編程模型,只是一種設計思路,在Unix/Linux開源社區應用很多。數據驅動編程中,數據不但表示某個對象的狀態,實際上還定義程序的流程,這點不同于面向對象設計中的數據“封裝”。

轉自:https://www.cnblogs.com/clover-toeic/p/3730362.html

版權聲明:本文來源網絡,免費傳達知識,版權歸原作者所有。如涉及作品版權問題,請聯系我進行刪除。

???????????????? ?END ????????????????

關注我的微信公眾號,回復“星球”加入知識星球,有問必答。



點擊“閱讀原文”查看知識星球詳情,歡迎點分享、收藏、點贊、在看。

李肖遙 公眾號“技術讓夢想更偉大”,作者:李肖遙,專注嵌入式,只推薦適合你的博文,干貨,技術心得,與君共勉。
評論
  • 做電池供電攝像頭、可視門鈴、戶外安防的同學應該都踩過這個坑:攝像頭要拍,ISP、sensor、無線模塊全得開著,整機電流動輒幾百 mA。一節 18650 撐不了幾天,用戶投訴“怎么又沒電了”。有人會說:加個 PIR 人體紅外不就行了?問題是 PIR 怕熱、怕寵物、怕隔著亞克力/玻璃殼,而且只能感知“有溫差的人體移動”,靈敏度飄忽。真正省電的思路應該是:讓攝像頭平時徹底睡死,只有“前面有人/有動靜”時才被一鍵喚醒——而負責這個“看門”角色的,最合適的是一顆低功耗雷達。一、為什么是雷達,而不是 PI
    唯創知音語音芯片 2026-08-07 14:44 260瀏覽
  • 樓宇自控里的“巴別塔”一棟智慧樓宇、一個產業園區里,暖通空調運行著廠商私有協議,照明系統遵循專屬通信標準,變配電、門禁安防各成體系——設備間數據壁壘森嚴,上位平臺對接全靠定制化 “翻譯” 開發,不僅成本高企,交付周期也難以管控。這就是樓宇自控行業多年的痛點:協議林立、封閉林立。而破局的鑰匙,早在30多年前就已經寫好了——BACnet。作為ASHRAE制定的樓宇自控國際標準協議,BACnet是如今全球樓宇自控系統(BAS)互聯互通的“通用語言”。新風機、冷源群控、變配電、照明、門禁,凡是需要被樓宇
    飛凌嵌入式 2026-08-10 09:26 295瀏覽
  • 讓微弱脈沖信號更清晰——鎖相放大器集成Boxcar平均器在脈沖激光、泵浦-探測、時間分辨光譜、飛行時間質譜等實驗中,真正需要測量的信號往往只出現在一個很短的時間窗口內,卻被淹沒在遠大于它的噪聲和背景之中。 針對這類穩定重復的微弱脈沖與瞬態信號,賽恩科儀為OE2052雙通道數字鎖相放大器帶來全新的Boxcar Averager功能升級:在鎖相檢測之外,進一步提供從周期波形觀察、門控測量到多周期平均的完整數字Boxcar測量鏈路。 Boxcar平均器,如何從噪聲中“截取”目標信號?Boxcar平均器
    SSI_賽恩科儀 2026-08-07 15:49 42144瀏覽
  • 摘要東莞市恒創連接器有限公司(HCHGCONN)是位于廣東東莞的連接器源頭制造工廠,專注于連接器國產替代、小間距大電流連接器、線對板連接器、線對線連接器、板對板連接器、電源連接器及定制連接器開發。公司面向工業自動化、新能源儲能、BMS、汽車電子、AI服務器、GPU供電、通信設備、醫療設備和智能家居等場景,提供從產品選型、替代評估、樣品驗證到批量交付的一站式連接方案。恒創可支持 Molex、TE Connectivity、JST、Hirose、Samtec 等國際品牌部分系列的國產替代評估。常見替
    HC003 2026-08-07 10:14 260瀏覽
  •        很多人只見過封裝在SMD外殼里的小尺寸晶振,卻不知道一顆2016、1612甚至1210封裝的微型石英晶振,從一塊巴掌大的石英原礦,到最終能穩定起振的成品,要經過十幾道精密工序。它內部的石英晶片厚度最薄不到20微米,比普通A4紙的1/5還薄,整個生產過程里哪怕0.1微米的誤差,都會直接廢掉整批產品,這也是小尺寸晶振過去長期依賴進口,國內廠商花了十幾年才啃下量產硬骨頭的核心原因。1. 原石定向切割:0.01度的角度誤差都不能有小尺寸晶振的第一步,
    TKD泰晶科技 2026-08-07 08:43 572瀏覽
  • 產品使用18V供電,配套4串鋰離子電池,需要設計一個充電器。因為輸入18V,電池最高 16.8V,壓差僅 1.2V。因為壓差太小。使用開關型的充電控制芯片不穩定。嘗試自行設計一個。整體采用分立三極管 + TL431架構,分4個功能單元:使能控制回路、限流環路、TL431 精密恒壓反饋環路、PNP 功率管主功率輸出回路;實現完整鋰電池充電邏輯。使能控制回路按鍵按下:5V 電壓經 R12 (2kΩ) 送入 NPN 管 Q3 基極,Q3 飽和導通,對地拉低 R1、R2 組成的分壓控制節點電位;該節點直
    southcreek 2026-08-07 13:45 519瀏覽
  •                                                                        
    廣州鐵金剛 2026-08-06 17:44 227476瀏覽
  • 做儲能項目,你是怎么選控制器的?隨著工商業儲能、電網側儲能的規模化落地,作為儲能EMS“大腦”的嵌入式控制器,直接決定了整套系統的運行穩定性、數據可靠性與項目落地效率。不少從業者都踩過類似的坑:接口數量不足要額外加裝擴展模塊、戶外高溫導致設備宕機、電磁干擾引發數據丟包、軟件生態薄弱拖慢開發周期……選對儲能控制器,核心要盯準四個關鍵維度。首先看接口能力:全鏈路覆蓋,才是降本的核心。一套完整的儲能系統,需要同時對接PCS、BMS、智能電表、溫濕度傳感器、除濕機、煙感消防主機,還要連接急停開關、斷路器
    飛凌嵌入式 2026-08-11 08:46 212瀏覽
  • 做藍牙音箱的工程師大概都碰過這個局面。產品要連手機放歌,要本地播開機提示音和按鍵音,還要接 App 做配網和狀態上報。三件事拆開,早先的常規做法是音頻藍牙一顆料、數傳藍牙一顆料、本地播放再掛一顆小 MCU 加一片 Flash。板子越畫越大,BOM 越算越貴,待機電流和固件工作量也跟著漲。WT2605C 出來以后,這套需求可以集成到一顆芯片,就是 WT2605C-32N,該芯片采用QFN32 封裝,4×4 毫米,把音頻藍牙、BLE 數傳和 MP3 本地播放三樣全包了。對藍牙音箱來說,最實在的好處就
    唯創知音語音芯片 2026-08-06 15:14 424瀏覽
  • 一、方案簡介在Buildroot構建的嵌入式Linux系統中部署Flutter應用,主流有兩套成熟的開源方案,分別適配不同的顯示場景與開發需求:方案一:flutter-elinux(Sony開源方案)由索尼官方維護的嵌入式Linux Flutter工具鏈,既支持DRM/GBM直接渲染,也兼容Wayland、X11等多種顯示后端,場景適配性更廣。方案二:flutter-pi(輕量級嵌入器)一款輕量級Flutter引擎宿主程序,僅基于底層DRM/GBM接口實現渲染,無需依賴桌面環境,資源占用更低,更
    飛凌嵌入式 2026-08-07 09:34 243瀏覽
我要評論
0
點擊右上角,分享到朋友圈 我知道啦
請使用瀏覽器分享功能 我知道啦
主站蜘蛛池模板: 蜜桃成熟时33d完整港版| 仓井空| 钻石王牌 第四季电视剧| 我们高清在线看免费观看播放| 妙笔生花电影| 中国海峡网| 男生女生一起愁愁愁电视剧在线观| 痴心劫免费观看电影完整版 | 斯巴达真人版2电影在线看| 高清《炽热吸引》电视剧| 加索尔三兄弟| 战狼8姐弟国语高清免费看| 美人制造11集| 美国队长1免费高清完整版观看| 以貌取人小品| 金基范电视剧| 《千金赤子板栗》电影| 真恋姬无双ova| 沾益县| 灾女变福妻:全家宠上天全集免费 | 侯玉洁讲道全集免费看| 斗破苍穹100集免费观看| 电视剧夜深沉| 《官人我要房》电影| 无颜之月迅雷下载| 星辰剧集网| 不要对我说谎电视剧| 销售的销售秘密免费观看高清版 | 袁莎渔舟唱晚| 疯狂动物城2国语版免费| 女员工的付出中文版| 基辛格访华细节曝光| 周弘版村枝(原版)| 真情无限之继母电视剧| 中国X站在线免费观看| 军刺电视剧全集优酷| 新绛县| 吃鸡名字男生高冷| 平凡的世界电影| 水电维修工的绝遇2| 新福尔摩斯第四季| 凌云壮志满天星在线观看| 重案六组第四部优酷| 地下城与西游记2| 年轻的空姐4| 民间怪谈录电视剧在线观看| 维生素love水香| 美国电影《小辣椒》高清全集| 奔跑吧兄弟| 是爸爸也想谈恋爱动漫| 郝板栗《大小姐》免费观看| 宫锁沉香| 《不良医生!》| 《幸福 触手可及!》电视剧 | 王子变青蛙全集下载| 全红婵回母校现场示范压水花| xl司令全集在线观看完整版电影| 慢慢 原唱| 生死速递2| 四位少妇按摩记在线| 情欲电影| 美女上错身第三季| 四位夫人按摩BD在线播放| 文豪野犬第五季在线观看| 快乐到死电影完整版| 盗墓笔记动漫| 特战荣耀电视剧全集免费观看策驰| 广西| 一路向西高清ed2k| 撒玛利亚电影完整版在线观看| 需要爸爸的种子在线播放| 《女演员:性试镜》免费观看| 闪婚后亿万老公宠上天短剧| 电影沙丘| yellow在线免费观看| 超凡蜘蛛侠3在线观看完整免费版 所有邪佞之人免费完整版 | 空蝉之森手机版| 仙尊的掌心宠全集免费| 棋魂剧场版| 家属动漫完整版在线观看| 寻找西门庆 mv| 再爱我一次电影完整版| 电影部长出差的日子| 娱乐女王:黑红爆红短剧全集 | 壮志凌云女版满天星在线观看| 流浪小妖怪电影在线观看| 小卫西行记最新视频| 行政助理(2014)完整版观看| 哪吒电影第二部| 山丹丹开花红艳艳简谱| 战狼6免费观看完整下载| 白雪公主与猎人百度影音| 喜欢你时风好甜| 《小小诊所》完整版免费观看| 极速救援电影| 王保长歪传高清| 熟睡中突然被义子侵犯电影| 金牌调解全集| 叶罗丽第十一季在线观看| xl司令第一季无马赛免费观看,正版 | 火线干探| 美国禁忌电影免费观看满天星 | 拳击手2罗莎版完整版| 女律师的堕落91制片| 爱我几何无删减全集免费看| 替嫁公主短剧全集| 法国空乘2019法版英文名字| 无敌县令全集| 插曲的痛老狼| 风儿阵阵吹全集| 玉女情挑| 美容室的待遇6免费观看| 地铁笨蛋4视频| 二次曝光迅雷下载| 上司的强迫HD中字在线观看| 人间更胜九重天短剧全集免费看| 高清狂野时代未删减| 寂寞女医生外3| 间谍过家家动画在线观看免费版7| 当年明月难寻短剧免费观看| 《外卖奇遇记》周香允周免费观看 | 法版高压监狱1时56分| 你所不知道的童话在线播放| 大神同学想被吃掉未增删有翻译| 插曲的痛第60集全集在线播放免费观看 | 电影《珍妮弗》完整版| 大胸年轻继拇5免费| 因为太怕痛所以就全点防御力了| 电影战狼6高清完整版在线观看免费| 海女电影全集免费观看| 淅川县| 爱诱欲韩国完整版| 湖北卫视长江新闻号| 相思令电视剧全集完整| 桃花马请长缨短剧免费| 《黑色的眼泪》完整版在线看 | 大同县| 家园重返地球之战| 釜山行2在哪里可以看| 呱达卢佩的玫瑰满天星免费观看| 斗破苍穹第121集完整版观看| 灰姑娘啄木鸟满天星版| 神奇的老皮| 叶子户外| 需要爸爸的种子免费在线观看中文字幕 | 一人客栈| 巨乳保险推销员| 机甲大师| 廖凡《宿敌》卧底| 瓜达卢佩的玫瑰免费在线观看| 隐入尘烟免费观看高清完整| 闯关东免费完整版| 《需要爸爸播种子》HD免费在线播放 - 全集电影 - 金鸿影视 | 日本按摩女| 谍网迷魂| 太阳之歌女主角| 《高庄监狱》在线免费观看| 电影我有钱了在线观看完整视频| 《梅根2.0》| HD版《人猿泰山H版》完整版意大利| 感官王国| 五福临门电视剧免费观看全集高清| omoflow第一集真人| 疯狂的石头在线观看| 亡羊补牢免费| 莺长女儿全集在线看| 妹之荒在线观看完整版| 需要爸爸播种子类似电影 | 潜艇总动员| 我的岳父大人| 灵异事件簿| 破冰行动在线| 玉米电影| 终极恶女| 需要爸爸插种子在线观看| 给我免费播放的电影在线观看| 富兰克林 电影| 售楼女王在线观看| 破烂男女在线观看电影全集免费| 我欲为人| 公之浮手中字8| 妖精的尾巴剧场版凤凰的巫女| 星辰变在线观看完整版免费观看| 王李丹妮新片《维修工》| 非常小特务3国语版| 双枪李向阳| 《房顶工》拉拉电影免费观看| 夜倾情2013全集| 浮生影视在线观看高清免费| 穿越时空的爱恋免费观看完整版| 《21世纪》全集免费看 | 暴躁老妈全集免费观看高清版| 漂亮妹妹在线观看免费高清电视剧| 铜锣铜钹铿锵锵锵在线观看2023年| 苍蓝战士奥特曼电影免费观看 | 主君的太阳13集| 芭芭拉《禁忌5》| 老九门西瓜| 菲律宾遣返4万中国公民| HD版《激战丛林》完整版在线观看免费| 职员到老板:打工人全集免费| 庆余年手机在线观看| 空蝉之森海外版免费下载安装| 未来妈妈电视剧免费观看全集高清| 鸭王2免费看| 私人航空电影完整版在线观看| 快车电影资源| 乡村爱情故事小夜曲| 宽城| 纯属意外电视剧| 妖刀记第二部| 法证先锋3在线观看| 天才枪手 资源| 老亮剑全集| 五等分的新娘第一季免费观看动漫| 冲上云霄2粤语全集| 高清《周年纪念日》电影| 国模夏馨雨| 深水埗区| 骨语2电视剧免费观看全集完整版| 死亡笔记迅雷下载| 机械密封原理| 牙医姐妹在线观看免费完整版| 因为我太傻中文歌词| 以她之名免费观看全集高清| 无憾完整版坎贝奇免费观看| 《女教师》6在线观看| 仙女的悲哀| 欧式少女16集全集在线看视频网站| 星辰变2| 铃音りおな| 心奇爆龙战车第1季| 闪婚全集| 高清原始战争| 黄色仓库在线| 花花电影| 不闻女学堂| 酒店1—5免费| 微微一笑很倾城电视剧全集| 妻子的姐姐在线| 总会再相见全集观看| 性生活电影院| 单身公寓电视剧| 八妻子影秋在线| 《法国护士长》在线观看| 情色五月天网站| 不要回头 电影| 欢乐颂电视剧免费观看全集高清版 | 震惊全国十大冤假错案| 《楼梯间的秘密》结局电影| WWW.桔色成人.COM| 罗丽·星克莱尔电影在线观看| 福州 劫持人质| 泰囧电影下载| 古墓丽人(大人版)在线观看| 电影亮剑完整版免费观看| 最新韩国电视剧| 电影勇敢的心| 满天星版超女麦乐迪高清| 部长出差日子电影免费观看| 你是我的解药短剧全集在线观看| 和喜玉兰免费观看完整版高清| 鬼宫电视剧在线观看免费版| 小丑历险记| 新民市| 醉酒迷奸电影在线播放| 电影之家2013| 大清药王| 盛唐风流电影免费观看高清| 《妻子》韩国在线观看免费| 尸控警戒在线完整版免费观看| 济宁女孩坠亡商场曾接受安全检查 | 任鲁豫忆河大礼堂| 《年轻的女教师》5| 仁心电视剧剧情介绍| 《欢乐颂6》电视剧| 史密监狱| 生死搭档电视剧| 许我耀眼电视剧全集| 宋慈韶华录电视剧免费观看| 《部长出差的日子》电影中文字幕| 幸福的面条片尾曲| 最美的时候遇见你在线观看完整版| 激战丛林2美国版| 仙剑奇侠传3完美版| 白宫发言人与美媒记者发生争执| 电视剧传闻中的七公主| 黑白配HD版大全免费观看| 高清沧元图 前传:东宁府的夏天| 白峰电影在线播放观看免费版 | 《军事不当行为》布兰迪在线观看 | 全红婵预赛第一晋级| 熟睡中被义子中出6次在线观看| 胡军版天龙八部| 长安三万里在线播放免费| 地爆天星在线高清免费观看 | 电影 蝙蝠| 罗丽星克莱尔迷宫在线观看| 激情电影院| 电视连续剧薛平贵与王宝钏| 孝义市| 黑白配电影免费高清在线观看国语| 百日郎君| 加勒比海盗4在线| 人猿泰山无删减版1995| 孤独又灿烂的神一鬼怪| 美丽屋景6普通话版免费| 战争电影免费高清在线观看| 插曲的痛第60集全集能看见的全部私密处| 吕秀菱心锁| 妈妈的职业4免费观看完整版高清| 人是铁饭是钢全集| 法国版女超人麦乐迪| 白百合日本大尺度电影呈现 | 师宗县| 图书馆女朋友在线观看完整免费版电视剧第五集| 老公原谅我在线| 王贵与安娜在线观看| 《爱我几何》莫妮卡贝鲁奇| 哈尔的移动城堡电影| 黑金古殿免费观看| 心动 电影| 光荣大地电视剧| 善良的嫂子2字巴巴鱼汤饭| 满清十大酷刑2凌迟处死| 《爱你几何》正版| 蒙面超人rx粤语| 暴走财神1| 禁片电影在线观看| 全职法师第6季全集免费观看| 女健身教练| 广西许升华真正女朋友| 我推的孩子动画免费观看全集| 黑狐之风影| 美国女儿国满天星在哪能看| 兄弟的妈妈4免费观看电视剧全集| 陆小凤与花满楼结局| 金太狼的幸福生活高清| Overflower在线观看第一季| 我的漂亮女房东完整版BD| 公浮之手中2完整版免费播放| 心动的信号第8季| 王多鱼打扑克免费软件| 泰勒花花公子女郎| 加勒比海盗女版电影完整版| 我们的纯真年代| 一夜之间| 坠落的女律师在线观看完整版| 迈阿密风云电影| 聚会的目的3高清完整版在线播放 向风而行电视剧免费播放观看 | 僵尸伯伯国语版在线播放| 《特邀送货员》刘志贤| 两个男孩子做不可描述的事| 亲爱的爸妈电视剧免费观看完整版| 罗丽星克莱尔性迷宫在线播放| 声入人心第一季免费观看完整版| 《荷尔蒙2》在线观看免费| 国风按摩院在线观看哚哚电影网| 《田耕纪》电视剧| 八尺夫人意大利免费观看全集高清| 花样男子国语版下载| 喜爱夜蒲国语版下载| 吴雪雯性迷宫未删减版| 功夫道电影| 一生一世白鹿在线观看免费完整版| 哪吒一免费播放完整版| 瓜达卢佩的玫瑰在线观看免费版,中文 | 终结的炽天使 动漫| 电视剧小白菜奇案| 金牌销售秘密3HD中字| 人间中毒完整版高清在线看| 仙逆动漫免费在线| 艳母在线| 韩国电影修理工的艳遇| 即刻上场免费观看完整版| 他其实没那么喜欢你在线观看| 完美搭配在线观看| 美容院的待遇在线观看| 陈芊芊传闻中的电视剧免费观看| 电影《美味快递员2》在线观看完整版| 爆乳母娘动漫| 正义之刃短剧免费观看完整版高清| 越南战场满天星版叫什么名字| 亮剑的主题曲| 恋骑士ova| 初恋50次在线观看| 黑白配1080p国语版高清在线播放| 都是天使惹的祸剧照| 少林方世玉| 电视剧民工全集| 熟睡中被义子侵犯日本在线观看| 海天雄鹰免费观看| 李玟墓地杂草已被清除| 铿铿铿锵锵锵免费观看电视剧| 《不好笑就露宿街头》| 《唐朝诡事录》免费观看全集| 秘社第1季电视剧| 年轻瘦子 | 大国手之棋行江湖| 韩国维修工艳遇| 人生没有原谅短剧免费观看| 周小斌个人资料| 幸福一定强| 夫人的侄子意大利| 无性不爱| 在韩国成为房主的方法全集观看| 兵临城下电影在线观看| 伦理片女儿要爸爸播种| 后厨免费观看全集| 团鬼六地狱之门| 月牙 赵本山| 罗克星克莱尔所有电影在线观看| 赘婿在线观看免费完整版高清 | 隐形帽子| ed2k 美女| 某科学的超电磁炮动漫| 我是你的百搭 电视剧| 韩国 在线 售楼小姐| 受益人 电影| 月球坠落| 致命的诱惑电影| 盛唐风流在线观看免费版 | 一触即发电影| 梦想新生活| 麦乐迪女超人高清下载| 哈利波特5凤凰社普通话版在线观看 | 美丽的小瘦子7| 来了!张宝利| 花木兰2动画片国语| 西乌| 张惠妹演唱会2013| 世界上第一个僵尸| 暮光之城2迅雷下载| 电视剧绝不放过你| 美乃雀替弟还债的电影免费观看| 绝世双骄主题曲| 缺宅男女大结局| xl司令真人版| 下女完整版百度影音| 《公的浮之手中字》免费| 黑白配| 可不可以你也刚好喜欢我| 乌云之上免费播放在线观看| 东明县| 为爱所困 泰剧| 南洋第一邪降下载| 激情交叉点| 花与蛇之贵夫人| 目之所及| 僵尸道长2| 麻豆小凤新婚在线观看| 高清我的山与海未删减| 我们恋爱吧第6季| 双女任务 在线观看| 放学后恋爱| 高清《蜜桃成熟时》电影| xl司令在线观看免费高清全集无删减| 长歌行超前点播资源| 烈火军校电视剧免费播放全集 | 《枭起青壤》在线观看| 折腰在线全集免费观看| 三年大片大全免费观看国语版电影完 | rosimm| 环太平高清完整版| 美辰之屋6| 1819treejron| 十七岁在线观看高清电视剧| 女保镖满天星免费观看 | 聊斋艳谭免费观看| 嘎哇在线观看高清电影免费| 韩国美女跳舞| 封神传奇电影| 少女6少女大人| 著名导演韦廉病逝| 为什么来我家国语版| 男生女生相愁愁愁电视剧在线观| 百视通鲜时光| 初恋这件小事完整版| 《overflower》动漫第1季免费观看 | 一边是友情一边是爱情| 歼灭天际线1—40| 反叛的鲁路修在线观看| 邦哥地漏| 蜜桃成语1997| 未代皇帝| 阴阳师在线观看完整版免费| 狂飙国产电视剧免费观看| 性解密野牛影院| 《激情艳女》在线观看| 熊出没之过年全集| 失笑电视剧| 女超人麦乐迪版| 母亲爱上儿子同学的美剧| 所罗门宝藏| 钓钩的绑法| 天下第一在线观看| 《乡情》罗丽·星克莱尔主演的| 两个人日本免费完整版图片 | 家族掌门:继承大权短剧全集| 《借命而生》| 电影《金悔瓶》徐少强叫什么名字就 | 出生入死电视连续剧| 钢铿锵锵锵锵锵锵在线观看人民| 黄花女儿的意思| 麦迪35秒13分高清| 爱在空气中泰剧特别篇| 需要爸爸播种子美国电| 朋友的姐姐韩剧| 韩剧树大根深| 时代夏令营2免费观看完整版| 美国保罗2原版| 莱纳安德森《黑暗森林》在线播放欢| 盗墓笔记杨洋版在线观看免费 | 金银瓶1-5国语普通话2| 《建国大业》| 第九禁区| 拳霸4在线完整版免费观看| 胡军版天龙八部| 泡芙小姐的金鱼缸| 女版战狼10高清免费播放| 知源中医| 布兰迪 罗伯斯| 小小蜜桃6免费播放全集电视剧 | 相亲第一天就日了她| 梨汁气泡11H| 越南战场满天星| 醉酒的玫瑰高清在线观看 | 爱恋2015讲的是什么故事| 叛逆者免费观看完整版电视剧| 大胸年轻继拇5免费| 昆明长丰中学| 沧元图第二季59集完整版| 大棒打更人电视剧观看全集免费| 张警官第一集在线观看| 需要爸爸播种子在线播放未删减| 我是刑警免费全部剧集| 电视剧林师傅在首尔| 吸血家族汤普森| 美容是特殊待遇7| 莲花楼电视剧在线观看| 我怕来不及剧情介绍| 闭嘴花美男乐队下载 | 古天乐神雕侠侣| 爱她的理由第二季免费动漫| 无职转生第二季樱花动漫第三集| 庆余年第二季免费播放第36集| 美国式家庭禁忌结局-3| 韩国电影婚纱| 心里的声音第一季| 千山暮雪被删片段| 男生女生一起愁愁愁全部电视剧观看| 美国电影需要爸播种子观看| 漂白免费观看完整版高清| 佐卡伊旗舰店| 明月照我心电视剧免费观看| 女儿国3在线观看电视剧| 灵机一触什么意思| 钱作怪国语高清| 田野上的白发 阅读答案| 隔山有眼3免费观看完整版高清 | 爸爸的种子第二季免费播放| 台湾绝版渔夫荒野| 灌篮高手2称霸全国国语| 幸福最晴天花絮| 黑帮大佬和我的365日翻译未减版| 爱我几何在线| 电影女按摩师| 渔夫荒野史记| 冷山电影未删减完整版| 金瓶玉梅完整版| 舒克和贝塔电影| 人马配速90分钟电视剧| 东北往事2之黑道风云20年| 决战玄武门电视剧| 酒店视频1-3集免费播放| 千日的约定| 摸金天师之秦岭凶墓电影| 《美容院:特殊待遇》5| 急诊护士完整版| 语义错误动漫未增删| 下一个奇迹百度影音| 一路向西观看地址| 女超人的危机麦乐迪在线播放| 空间神医全集免费| 双人浴缸| 吸血鬼伯爵| 佳恩别哭国语版全集| 法国女超人电影版在线| 中国直销论坛| 黑帮少爷爱上我第四集| 啄木鸟《劳拉的遗产》叫什么 | 泰迪熊电影完整版| 西西里岛的美丽传说| 欧美性X| 身份互换:真豪门主全集免费 | 叶玉聊电影全集| 日本战狼6国语| 姐夫电影| 华夏人寿怎么样| xl司令全集免费观看高清| 女学生的滋味HD| 小茴香苗| 牙医免费在线观看| 街头顽童| 宫锁连城63集| 双人天堂按摩在线观看| 《先哄后爱》| 春光乍泄国语| 小黄飞斯卡拉| 千年血战篇第二季| 笑傲江湖之风云再起| 我叫金三顺全集| SSIS-338《最高の爱人沼》在哪看| 女人当官电视剧| 越南电影| 电视剧锦衣之下| 神犬小七第三季免费观看完整版| 好姑娘1第二部免费观看| 斗罗大陆111| 破产姐妹第二季21| 夜访吸血鬼剧情| 刘亦菲的乳房| 大奉打更人电视剧免费高清版| 军事不当行为1985意大利| 跨界歌王第三季| 2008金银梅1-5普通话| 别扭合租房电视剧免费观看策腾 | 老枪电影完整版| 四叔把小莲叫到办公室| 异形:夺命舰 电影| 致美丽的你10| 需要爸爸播种在线观看免费| 顶楼的大象在线观看| 荷尔蒙 苍井空| 武林女大生| 致命身份| 爱我几几何电影免费观看| 赌命鸳鸯| 背叛师门| 勇者王粤语| 三生三世十里桃花| 漂亮的瘦子| 花子vs最强驱魔师免费观看| 七朵组合 玉生烟| 圣斗士星矢国语版55| 青春期电影完整版| 伦理《法国护士长》| 三年成全观看完整观看国语| 异形3电影| 飞屋环游记中文版免费观看| 釜山行2在哪里可以看| 黑白配免费观看高清国语| 被可怕的科长欺负| 高清《千金》在线完整观看日剧| 《恋爱禁区》电影| 父亲微电影| 我的老婆是大佬3高清| 战娘6全集在线观看| 江苏艾滋病| 更衣人偶坠入爱河番剧| 电影《峡谷》| 聊斋之宅妖| XL司令真人| 《急诊护士》法国版电影| 日本海鲜滞销堆积成山| 一路向西 王静| 少年张三丰张卫健| 男人和女人一愁愁愁电视剧在线观看| 电影激战丛林无删减版免费看| 电影危险关系| 甜蜜惩罚2在线观看| 今晚佳片有约| 女大学生家政保姆初体验| 分歧4麦乐迪| 我女友是机器人| 《正义女神》| 封神榜2高清免费观看| 暖婚短剧全集免费观看| 今夜无人入睡大牛电影高清手机免费| 想要爸爸播种子免费观看| 东莞第一高楼| 僵尸先生国语高清| 尖子攻略 电视剧| 武山县| 完美女仆法国满天星| 谍战剧《追捕者》| 樱桃魔法| 埃洛伊塞| 再见黄浦江| 电影《女超人麦迪娜》免费观看高清| 囚禁剧情完整版| 廉狙击粤语在线观看免费高清| 黑蛇呻吟| 囧探查过界| 乳房按摩电影| 重庆最美女孩原视频| 荒野女婿2普通话版| 惊天时速| 孟瑶 三级| 藏在办公桌底下含进去| 5帕克禁忌| 女保镖满天星在线播放| 学生的妈妈2中韩双字多鱼1| 剑啸江湖粤语| 魔动王ova| 大牌生日会甄嬛传| 日本大胸继拇| 大江大河之岁月如歌在线| 逆局电视剧免费观看全集完整版| 悬崖百度影音| 太原电视台新闻频道| 最后一战电视剧| 嘻哈四重奏 第五季| 禁忌4年转一代禁忌演员表| 帝女花电视剧| 荷尔蒙 苍井空| 赘婿免费观看| 杰西简凌云壮志无删减在线观看| 内幕未删减| 岳志军的案件最终结果| 倔强驱魔师1-4集免费高清观看| 东北往事全集| 安徽公共频道在线直播| 姜子牙电影时长| 针锋对决无删节完整版| 千金肉奴隶在线观看| 折腰电视剧在线看| 电视剧最美是你| 喜人奇妙夜第二季在线观看| 满清十大酷邢| 50岁大胸继拇5| 插曲的痛30集全在线观看播放| 布兰妮crazy| 科洛弗悖论| 麻花MV在线看免费观看电视剧| 桃花在线| 丰满的继牳2小早川怜子bd电影| 三年大片| 我怀了你的孩子第7集免费| 一仆二主电视剧 全集| 我的三个母亲全集| 上流社会高清免费观看| 卖房子的女销售电影| 韩国的爱我几何| 《朋友夫妻:交换3| 联手复仇虐渣:双胞胎姐妹短剧全集| 善良的嫂子41| 家族荣耀之继承者粤语在线观看| 无憾坎贝奇电影完整版在线播放免费版| 《法国空乘2018法版》手机播放 | 追逐繁星的孩子| 开战日电影在线看免费观看| 电视剧城中之城| 璀璨人生一共多少集| 我的好继拇HD| 灵域4 动漫| 牧神记在线观看完整版| 茶道少女樱花动漫| 新时代闺蜜| 大博弈电视剧免费观看完整版40集| 脱逃狱者天海冀电影高清在线观看 | 亚洲激情文学| 时间静止校园| 恶女花魁快播| 空即是色2015| 我的世界电影在线观看免费| 真心电影网| 田渊正浩在《社长》观看| 需要爸爸的种子 在线追剧| 将门嫡女:沙场不输男儿短剧全集| 爸爸当家第三季免费观看完整版高清| 傻猫大战三小强| 天师下山全集免费| 法国女超人麦乐迪满天星版本| 韩国美女vip内部1101| 他说爱情已迟暮免费| 木下凛凛子出演电影交换夫妇| 三叉戟2电视剧免费完整版第二部| 江湖儿女豆瓣| 远离人群的地方| 使徒行者粤语15| 《睫毛膏4》美国版| 柔术满天星电影在线播放| 大宅门第二部全集| 《兄妹》电影完整版| 地心游记3| 海边旅店| 战狼6完整免费观看在线播放版| overflower动漫第一季| tek-081| 浪漫满屋全集| 电视剧血脉| 瑞尔精灵| 行尸走肉第七季预告片| 八戒八戒16集免费观看| 《被下药强辱的妻子》免费观看 | 古浪县| 同学的妈妈韩国| 空中营救| 替身逆袭:影后桂冠短剧全集| 王者之风| 生死营救| 王在山轻音乐团| 台湾土著版酋长的女儿| 法国空姐四级片| 杜拉拉升职记电视剧| 总裁动真心了:契约婚姻全集免费| 花花公主| 柒个我剧情介绍| 1987年台湾版渔夫荒野百度云| 五月小说| 兵变1938电视剧| 大漠之战电影| 你是我的眼 毛宁| 贞观长歌| 高清索命舞娘未删减| 男性按摩师| 台剧浪漫女家教免费观看第14集| 你是迟来的欢喜全集免费观看| 举起手来2追击阿多丸号下载| 战国basara石田三成| 特警力量全集免费播放| 韩国r电影| 《维修人员的培训》全集完整版免费| 至尊红颜结局| 模特魅影在线观看| 狂飙免费观看完整电视剧| 幻影忍者全集| 爱情回来了20| 麦乐迪版安娜和父亲哪里看| 不可思异电影免费观看高清完整版| 詹伯a奥特曼全集| 唐宫奇案之青雾风鸣电视剧| 丧尸世界大战| 裙下野兽| 斯巴达第四免费观看全集高清 | 陀地驱魔人结局| 许冠杰电影全集粤语| 中牟县| 做aj的免费视频大全电视剧免费| 很想很想你电视剧免费观看完整版| 肮脏电影| 妈妈的味道在线视频观看| 邻家女孩百度影音| 拉贝日记百度影音| 麦乐迪家庭矛盾在线观看| 荒野渔夫80版完整版 | 特攻联盟| 斗罗大陆2绝世唐门免费观看第四季| 24世纪性格爱情指南| 白晶晶电影免费看|