[嵌入式开发模块]环形缓冲区/循环队列C语言面向对象实现(四)Queue类源码

要使用这个模块,得先放入Buffer类相关代码,详见前几篇博客。

Queue

Queue.h

/*
*******************************************************************************************
*
*                                Parent Class for All Queue
*
* File : Queue.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/02/16
* version: V1.0
* History: 
* NOTE(s): a. Queue is defined here as a FIFO(First In First Out) struct, which has In
*             and Out methods. It's inherited from Container class.
*******************************************************************************************
*/

#ifndef _QUEUE_H
#define _QUEUE_H

/*
*******************************************************************************************
*                                        DATA TYPE
*******************************************************************************************
*/

typedef struct QUEUE_STRUCT       * Queue;
typedef struct QUEUEUINT8_STRUCT  * QueueUINT8;
typedef struct QUEUEUINT16_STRUCT * QueueUINT16;
typedef struct QUEUEUINT32_STRUCT * QueueUINT32;

#include "Container.h"

typedef void (* QUEUEINTERFACE_VOID)(Queue);
typedef void (* QUEUEUINT8INTERFACE_VOID)(QueueUINT8);
typedef void (* QUEUEUINT16INTERFACE_VOID)(QueueUINT16);
typedef void (* QUEUEUINT32INTERFACE_VOID)(QueueUINT32);

typedef void (* QUEUEUINT8INTERFACE_TAKEELEMENT)(QueueUINT8,uint8_t);
typedef void (* QUEUEUINT16INTERFACE_TAKEELEMENT)(QueueUINT16,uint16_t);
typedef void (* QUEUEUINT32INTERFACE_TAKEELEMENT)(QueueUINT32,uint32_t);

typedef uint8_t (* QUEUEUINT8INTERFACE_RETURNELEMENT)(QueueUINT8);
typedef uint16_t (* QUEUEUINT16INTERFACE_RETURNELEMENT)(QueueUINT16);
typedef uint32_t (* QUEUEUINT32INTERFACE_RETURNELEMENT)(QueueUINT32);

/*
*******************************************************************************************
*                                       CONSTANT
*******************************************************************************************
*/
// default return value when any error in methods of child class
#define QUEUE_WHENERR_ELEMENTRETURN       -1
/*
*******************************************************************************************
*                               INTERFACE FOR ALL QUEUE CLASS
*******************************************************************************************
*/

// unsigned int Queue_getCapacity(Queue que);
// return: capacity of the Queue que
//         0      when que == NULL
#define Queue_getCapacity(que)              Container_getCapacity((Container)(que))
// unsigned int Queue_getCount(Queue que);
// return: element count of the Queue que
//         0      when que == NULL
#define Queue_getCount(que)                 Container_getCount((Container)(que))
// BOOL Queue_isEmpty(Queue que);
// return: whether the Queue que is empty
//         TRUE   when que == NULL
#define Queue_isEmpty(que)                  Container_isEmpty((Container)(que))
// BOOL Queue_isFull(Queue que);
// return: whether the Queue que is full
//         FALSE  when que == NULL
#define Queue_isFull(que)                   Container_isFull((Container)(que))
// void Queue_Destroy(Queue que);
// destroy the Queue que
#define Queue_Destroy(que)                  Container_Destroy((Container)(que))
// cleanup the Queue que
void Queue_Cleanup(Queue que);
/*
*******************************************************************************************
*                              INTERFACE FOR UINT8 QUEUE
*******************************************************************************************
*/

// unsigned int QueueUINT8_getCapacity(QueueUINT8 que);
#define QueueUINT8_getCapacity(que)          Queue_getCapacity((Queue)que)
// unsigned int QueueUINT8_getCount(QueueUINT8 que);
#define QueueUINT8_getCount(que)             Queue_getCount((Queue)que)
// BOOL QueueUINT8_isEmpty(QueueUINT8 que);
#define QueueUINT8_isEmpty(que)              Queue_isEmpty((Queue)que)
// BOOL QueueUINT8_isFull(QueueUINT8 que);
#define QueueUINT8_isFull(que)               Queue_isFull((Queue)que)
// void QueueUINT8_Destroy(QueueUINT8 que);
#define QueueUINT8_Destroy(que)              Queue_Destroy((Queue)que)
// put an element to the queue.
// if container is full, the behavior depends on the implementation.
void    QueueUINT8_In (QueueUINT8 que,uint8_t element);
// pick an element from the queue.
// return QUEUE_WHENERR_ELEMENTRETURN when any error
uint8_t QueueUINT8_Out  (QueueUINT8 que);
#define QueueUINT8_Cleanup(que)             Queue_Cleanup((Queue)que)

/*
*******************************************************************************************
*                              INTERFACE FOR UINT16 QUEUE
*******************************************************************************************
*/

// unsigned int QueueUINT16_getCapacity(QueueUINT16 que);
#define QueueUINT16_getCapacity(que)          Queue_getCapacity((Queue)que)
// unsigned int QueueUINT16_getCount(QueueUINT16 que);
#define QueueUINT16_getCount(que)             Queue_getCount((Queue)que)
// BOOL QueueUINT16_isEmpty(QueueUINT16 que);
#define QueueUINT16_isEmpty(que)              Queue_isEmpty((Queue)que)
// BOOL QueueUINT16_isFull(QueueUINT16 que);
#define QueueUINT16_isFull(que)               Queue_isFull((Queue)que)
// void QueueUINT16_Destroy(QueueUINT16 que);
#define QueueUINT16_Destroy(que)              Queue_Destroy((Queue)que)
// put an element to the queue.
// if container is full, the behavior depends on the implementation.
void    QueueUINT16_In (QueueUINT16 que,uint16_t element);
// pick an element from the queue.
// return QUEUE_WHENERR_ELEMENTRETURN when any error
uint16_t QueueUINT16_Out  (QueueUINT16 que);
#define QueueUINT16_Cleanup(que)             Queue_Cleanup((Queue)que)

/*
*******************************************************************************************
*                              INTERFACE FOR UINT32 QUEUE
*******************************************************************************************
*/

// unsigned int QueueUINT32_getCapacity(QueueUINT32 que);
#define QueueUINT32_getCapacity(que)          Queue_getCapacity((Queue)que)
// unsigned int QueueUINT32_getCount(QueueUINT32 que);
#define QueueUINT32_getCount(que)             Queue_getCount((Queue)que)
// BOOL QueueUINT32_isEmpty(QueueUINT32 que);
#define QueueUINT32_isEmpty(que)              Queue_isEmpty((Queue)que)
// BOOL QueueUINT32_isFull(QueueUINT32 que);
#define QueueUINT32_isFull(que)               Queue_isFull((Queue)que)
// void QueueUINT32_Destroy(QueueUINT32 que);
#define QueueUINT32_Destroy(que)              Queue_Destroy((Queue)que)
// put an element to the queue.
// if container is full, the behavior depends on the implementation.
void    QueueUINT32_In (QueueUINT32 que,uint32_t element);
// pick an element from the queue.
// return QUEUE_WHENERR_ELEMENTRETURN when any error
uint32_t QueueUINT32_Out  (QueueUINT32 que);
#define QueueUINT32_Cleanup(que)             Queue_Cleanup((Queue)que)

#include "QueuePrivate.h"
#endif

QueuePrivate.h

/*
*******************************************************************************************
*
*                                Parent Class for All Queue
*
* File : QueuePrivate.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/02/16
* version: V1.0
* History: 
* NOTE(s): a. this file is only for the developer, user shouldn't use any in this file.
*          b. to inherit from parent class in C:
*
*             #include "ParentClass.h"
*             typedef struct CHILDCLASS_STRUCT{
*               PARENTCLASS_STRUCT parent;
*               SOMETYPE           memberofchild;
*               ...
*             } * ChildClass;
*
*             to use the interface of ParentClass, e.g:
*             
*             ChildClass child = ChildClass_Create();
*             PARENTCLASS_METHOD((ParentClass)child,...);
*
*          c. Main purpose of this class is to provide common interface for Queue of all 
*             type. All Queue Objects have the same shape interfaces (in the virtual 
*             table) following the exact same order below:
*             
*             void        QueueXXXX_In     (Queue que, ElementType element);
*             ElementType QueueXXXX_Out    (Queue que);
*             void        QueueXXXX_Cleanup(Queue que);
* 
*             Different ElementTypes lead to different function signatures, however, due to
*             the exact same order, we know which one is the "In" function of all function.
*          d. If you want to add a Queue Class of new ElementType, you need to implement
*             some methods to guide compiler to correct function signatures, just like 
*                     uint8_t QueueUINT8_Out(QueueUINT8 buf);
*             do. You can use Implementations of QueueUINT8 Interface as a template to
*             create yours.
*          e. If you want to write an implementation for QueueUINT8 Class, for example. 
*             the main work you should do is to inherit from the Queue Class, implement
*             the CONTAINER_INTERFACE and QUEUE_INTERFACE. And when creating the instance,
*             assign the virtual table. (see _Queue_Init() below)
*             Then, when user call the interface, the corresponding interface will be called,
*             there is no need for NULL check in your interface implementation.
*          f. To reduce redundancy, it's recommended to adapt from the exist Queue Class,
*             whose element type take the same space as your new type, rather than create 
*             interfaces for new type. e.g,QueueChar can be adapted from QueueUINT8.
*          g. developer should implement the whole vtable, none of the interfaces in the
*             vtable can be left NULL. There will be no NULL check for vtable.
*          h. !!! the manners of In method when full ,and Out method when empty, are !!!
*             !!! undefined. It depends on the impelementation !!!
*******************************************************************************************
*/
#include "Queue.h"

/*
*******************************************************************************************
*                                  PROTECTED   INTERFACES
*******************************************************************************************
*/
typedef const struct QUEUE_INTERFACE       * QUEUE_INTERFACE_TABLE;
typedef const struct QUEUEUINT8_INTERFACE  * QUEUEUINT8_INTERFACE_TABLE;
typedef const struct QUEUEUINT16_INTERFACE * QUEUEUINT16_INTERFACE_TABLE;
typedef const struct QUEUEUINT32_INTERFACE * QUEUEUINT32_INTERFACE_TABLE;

typedef struct QUEUE_STRUCT{
  CONTAINER_STRUCT parent;
  union{
    QUEUE_INTERFACE_TABLE       v;
    QUEUEUINT8_INTERFACE_TABLE  uint8;
    QUEUEUINT16_INTERFACE_TABLE uint16;
    QUEUEUINT32_INTERFACE_TABLE uint32;
  } vtable;
}QUEUE_STRUCT;

#define _Queue_mVtable(que)    (((Queue)(que))->vtable.v)
#define _Queue_fIn(que)        (_Queue_mVtable(que)->in)
#define _Queue_fOut(que)       (_Queue_mVtable(que)->out)
#define _Queue_fCleanup(que)   (_Queue_mVtable(que)->cleanup)

typedef struct  QUEUEUINT8_STRUCT{
  QUEUE_STRUCT member;
}QUEUEUINT8_STRUCT;

#define _QueueUINT8_mVtable(que)         (((Queue)(que))->vtable.uint8)
#define _QueueUINT8_fIn(que)             (_QueueUINT8_mVtable(que)->in)
#define _QueueUINT8_fOut(que)            (_QueueUINT8_mVtable(que)->out)
#define _QueueUINT8_fCleanup(que)        (_QueueUINT8_mVtable(que)->cleanup)

typedef struct  QUEUEUINT16_STRUCT{
  QUEUE_STRUCT member;
}QUEUEUINT16_STRUCT;

#define _QueueUINT16_mVtable(que)         (((Queue)(que))->vtable.uint16)
#define _QueueUINT16_fIn(que)             (_QueueUINT16_mVtable(que)->in)
#define _QueueUINT16_fOut(que)            (_QueueUINT16_mVtable(que)->out)
#define _QueueUINT16_fCleanup(que)        (_QueueUINT16_mVtable(que)->cleanup)

typedef struct  QUEUEUINT32_STRUCT{
  QUEUE_STRUCT member;
}QUEUEUINT32_STRUCT;

#define _QueueUINT32_mVtable(que)         (((Queue)(que))->vtable.uint32)
#define _QueueUINT32_fIn(que)             (_QueueUINT32_mVtable(que)->in)
#define _QueueUINT32_fOut(que)            (_QueueUINT32_mVtable(que)->out)
#define _QueueUINT32_fCleanup(que)        (_QueueUINT32_mVtable(que)->cleanup)

typedef void (* QUEUEINTERFACE_POINTER)(void);
typedef struct QUEUE_INTERFACE{
  QUEUEINTERFACE_POINTER   in;          // the que passed in is assured not NULL
  QUEUEINTERFACE_POINTER   out;         // the que passed in is assured not NULL
  QUEUEINTERFACE_VOID      cleanup;     // the que passed in is assured not NULL
} QUEUE_INTERFACE;

typedef struct QUEUEUINT8_INTERFACE{
  QUEUEUINT8INTERFACE_TAKEELEMENT   in;      // the que passed in is assured not NULL
  QUEUEUINT8INTERFACE_RETURNELEMENT out;     // the que passed in is assured not NULL
  QUEUEUINT8INTERFACE_VOID          cleanup; // the que passed in is assured not NULL
} QUEUEUINT8_INTERFACE;

typedef struct QUEUEUINT16_INTERFACE{
  QUEUEUINT16INTERFACE_TAKEELEMENT   in;      // the que passed in is assured not NULL
  QUEUEUINT16INTERFACE_RETURNELEMENT out;     // the que passed in is assured not NULL
  QUEUEUINT16INTERFACE_VOID          cleanup; // the que passed in is assured not NULL
} QUEUEUINT16_INTERFACE;

typedef struct QUEUEUINT32_INTERFACE{
  QUEUEUINT32INTERFACE_TAKEELEMENT   in;      // the que passed in is assured not NULL
  QUEUEUINT32INTERFACE_RETURNELEMENT out;     // the que passed in is assured not NULL
  QUEUEUINT32INTERFACE_VOID          cleanup; // the que passed in is assured not NULL
} QUEUEUINT32_INTERFACE;

/*
*******************************************************************************************
*                            PROTECTED  MEMBER  AND  METHOD
*******************************************************************************************
*/
// initialize a Queue instance(internal, no NULL check)
// void _Queue_Init(Queue que,OBJECTINTERFACE_VOID fDestroy,CONTAINER_INTERFACE_TABLE c_vtable,
//                  QUEUE_INTERFACE_TABLE vtable);
#define _Queue_Init(que,fDestroy,c_vtable,vtable)  \
{ _Container_Init(que,fDestroy,c_vtable); \
  _Queue_mVtable(que) = vtable; }
// destroy method member of the quefer(internal, no NULL check)
#define _Queue_fDestroy(que)         (_Container_fDestroy((Container)(que)))
#define _Queue_getCapacity(que)      _Container_getCapacity((Container)(que))
#define _Queue_getCount(que)         _Container_getCount((Container)(que))
// whether the quefer is empty(internal, no NULL check)
#define _Queue_isEmpty(que)          _Container_isEmpty((Container)(que))
// whether the quefer is full(internal, no NULL check)
#define _Queue_isFull(que)           _Container_isFull((Container)(que))

//BOOL _Queue_isNullorEmpty(Queue que);
#define _Queue_isNullorEmpty(que)    _Container_isNullorEmpty((Container)(que))
//BOOL _Queue_isNullorFull(Queue que);
#define _Queue_isNullorFull(que)     _Container_isNullorFull((Container)(que))

Queue.c

/*
*******************************************************************************************
*
*    Common Methods for All Queue Class and implementations for UINT8, UINT16, UINT32
*
* File : Queue.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/02/16
* version: V1.0
* History: 
* NOTE(s): 1. we assume every queue should implement the (whole) vtable, so there is no
*             NULL check for vtable and the method. (if a child class don't implementation 
*             a method and leave it NULL, and it happens to be called, then ,just let it
*             crash!! hhhhh)
*******************************************************************************************
*/

#include "Queue.h"

/*
*******************************************************************************************
*                               PUBLIC METHOD IMPLEMENTATIONS
*******************************************************************************************
*/

void Queue_Cleanup(Queue que){
  if(que == NULL)
    return;
  _Queue_fCleanup(que)(que);
}

void QueueUINT8_In(QueueUINT8 que,uint8_t element){
  if(que == NULL)
    return;
  _QueueUINT8_fIn(que)(que,element);
}

uint8_t QueueUINT8_Out(QueueUINT8 que){
  if(que == NULL)
    return QUEUE_WHENERR_ELEMENTRETURN;
  return _QueueUINT8_fOut(que)(que);
}

void QueueUINT16_In(QueueUINT16 que,uint16_t element){
  if(que == NULL)
    return;
  _QueueUINT16_fIn(que)(que,element);
}

uint16_t QueueUINT16_Out(QueueUINT16 que){
  if(que == NULL)
    return QUEUE_WHENERR_ELEMENTRETURN;
  return _QueueUINT16_fOut(que)(que);
}

void QueueUINT32_In(QueueUINT32 que,uint32_t element){
  if(que == NULL)
    return;
  _QueueUINT32_fIn(que)(que,element);
}

uint32_t QueueUINT32_Out(QueueUINT32 que){
  if(que == NULL)
    return QUEUE_WHENERR_ELEMENTRETURN;
  return _QueueUINT32_fOut(que)(que);
}

BufferToQueueAdapter

BufferToQueueAdapter.h

/*
*******************************************************************************************
*
*                             Create a Queue from a Buffer
*
* File : BufferToQueueAdapter.h
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/02/16
* version: V1.0
* History: 
* NOTE(s): a. This Adapter is to adapt a Buffer instance to a Queue instance
*          b. once you has adapted a Buffer instance to Queue, you should think the 
*             instance private to the new created Queue instance and never use it, 
*             the new instance will destroy the buffer instance when destroyed.
*******************************************************************************************
*/
#ifndef _BUFFERTOQUEUEADAPTER_H
#define _BUFFERTOQUEUEADAPTER_H

#include "Buffer.h"
#include "Queue.h"
/*
*******************************************************************************************
*                                     INTERFACE 
*******************************************************************************************
*/
// create a queue instance from a buffer instance 
// return :  the new created queue instance.
//           NULL    if any error.
// note   :  if success, you should never use the buffer instance.
QueueUINT8  BufferToQueueAdapter_CreateUINT8 (BufferUINT8  buf);
QueueUINT16 BufferToQueueAdapter_CreateUINT16(BufferUINT16 buf);
QueueUINT32 BufferToQueueAdapter_CreateUINT32(BufferUINT32 buf);

#endif

BufferToQueueAdapter.c

/*
*******************************************************************************************
*
*                         Create a Queue from a Buffer Implementation
*
* File : BufferToQueueAdapter.c
* By   : Lin Shijun(http://blog.csdn.net/lin_strong)
* Date:  2019/02/16
* version: V1.0
* History: 
* NOTE(s): the main work of this adapter is to provide interface to redirect the call to
*          the method of queue to the corresponding buffer method.
*                     Queue_getCapacity    -->    Buffer_getCapacity
*                     Queue_getCount       -->    Buffer_getCount
*                     Queue_In             -->    Buffer_BackIn
*                     Queue_Out            -->    Buffer_FrontOut
*                     Queue_Cleanup        -->    Buffer_Cleanup
*******************************************************************************************
*/
#include "BufferToQueueAdapter.h"
#include <stdlib.h>

/*
*******************************************************************************************
*                                 DATA STRUCT DEFINITION
*******************************************************************************************
*/
typedef struct QUEUEFROMBUFFER_STRUCT{
  QUEUE_STRUCT parent;
  Buffer buf;
}QUEUEFROMBUFFER_STRUCT, *QueueFromBuffer;

#define _QueueFromBuffer_mBuf(que)     (((QueueFromBuffer)que)->buf)
#define _mBuf(que)     _QueueFromBuffer_mBuf(que)
  
/*
*******************************************************************************************
*                               INTERFACE IMPLEMENTATIONS
*******************************************************************************************
*/
static void _cleanup(Queue que){
  _Buffer_fCleanup(_mBuf(que))(_mBuf(que));
}

static void uint8_in(QueueUINT8 que,uint8_t element){
  ((BUFFERUINT8INTERFACE_TAKEELEMENT)_BufferUINT8_fBackIn(_mBuf(que)))
    ((BufferUINT8)_mBuf(que),element);
}

static uint8_t uint8_out(QueueUINT8 que){
  return ((BUFFERUINT8INTERFACE_RETURNELEMENT)_BufferUINT8_fFrontOut(_mBuf(que)))((BufferUINT8)_mBuf(que));
}

static const QUEUEUINT8_INTERFACE queueuint8_interface = {
  uint8_in,
  uint8_out,
  (QUEUEUINT8INTERFACE_VOID)_cleanup
};

static void uint16_in(QueueUINT16 que,uint16_t element){
  ((BUFFERUINT16INTERFACE_TAKEELEMENT)_BufferUINT16_fBackIn(_mBuf(que)))
    ((BufferUINT16)_mBuf(que),element);
}

static uint16_t uint16_out(QueueUINT16 que){
  return ((BUFFERUINT16INTERFACE_RETURNELEMENT)_BufferUINT16_fFrontOut(_mBuf(que)))((BufferUINT16)_mBuf(que));
}

static const QUEUEUINT16_INTERFACE queueuint16_interface = {
  uint16_in,
  uint16_out,
  (QUEUEUINT16INTERFACE_VOID)_cleanup
};

static void uint32_in(QueueUINT32 que,uint32_t element){
  ((BUFFERUINT32INTERFACE_TAKEELEMENT)_BufferUINT32_fBackIn(_mBuf(que)))
    ((BufferUINT32)_mBuf(que),element);
}

static uint32_t uint32_out(QueueUINT32 que){
  return ((BUFFERUINT32INTERFACE_RETURNELEMENT)_BufferUINT32_fFrontOut(_mBuf(que)))((BufferUINT32)_mBuf(que));
}

static const QUEUEUINT32_INTERFACE queueuint32_interface = {
  uint32_in,
  uint32_out,
  (QUEUEUINT32INTERFACE_VOID)_cleanup
};

static unsigned int _getCapacity(Container ct){
  return _Buffer_getCapacity(_mBuf(ct));
}

static unsigned int _getCount(Container ct){
  return _Buffer_getCount(_mBuf(ct));
}
static const CONTAINER_INTERFACE _container_interface = {
  _getCapacity,
  _getCount
};

static void _destroy(void * q){
  if(q){
    Buffer_Destroy(_mBuf(q));
    free(q);
  }
}
/*
*******************************************************************************************
*                               PUBLIC METHOD IMPLEMENTATIONS
*******************************************************************************************
*/

QueueUINT8 BufferToQueueAdapter_CreateUINT8(BufferUINT8 buf){
  QueueFromBuffer q;
  if(buf == NULL || (q = (QueueFromBuffer)malloc(sizeof(QUEUEFROMBUFFER_STRUCT))) == NULL)
    return NULL;
  _mBuf(q) = (Buffer)buf;
  _Queue_Init(q,_destroy,&_container_interface,(QUEUE_INTERFACE_TABLE)&queueuint8_interface);
  return (QueueUINT8)q;
}

 
QueueUINT16 BufferToQueueAdapter_CreateUINT16(BufferUINT16 buf){
  QueueFromBuffer q;
  if(buf == NULL || (q = (QueueFromBuffer)malloc(sizeof(QUEUEFROMBUFFER_STRUCT))) == NULL)
    return NULL;
  _mBuf(q) = (Buffer)buf;
  _Queue_Init(q,_destroy,&_container_interface,(QUEUE_INTERFACE_TABLE)&queueuint16_interface);
  return (QueueUINT16)q;
}

QueueUINT32 BufferToQueueAdapter_CreateUINT32(BufferUINT32 buf){
  QueueFromBuffer q;
  if(buf == NULL || (q = (QueueFromBuffer)malloc(sizeof(QUEUEFROMBUFFER_STRUCT))) == NULL)
    return NULL;
  _mBuf(q) = (Buffer)buf;
  _Queue_Init(q,_destroy,&_container_interface,(QUEUE_INTERFACE_TABLE)&queueuint32_interface);
  return (QueueUINT32)q;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值