readwrite,readonly,assign,ret…

本文详细介绍了Objective-C中@property的使用方法及其属性,包括getter与setter的定制、访问级别设置、赋值行为(retain, copy, assign)及原子性访问控制等。

<wbr></wbr>

<wbr></wbr>


@property是一个属性访问声明,扩号内支持以下几个属性:


1getter=getterNamesetter=setterName,设置settergetter的方法名


2readwrite,readonly,设置可供访问级别


3assignsetter方法直接赋值,不进行任何retain操作,为了解决原类
型与环循引用问题


4retainsetter方法对参数进行release旧值再retain新值,所有实现
都是这个顺序(CC上有相关资料)


5copysetter方法进行Copy操作,与retain处理流程一样,先旧值
release
,再Copy出新的对象,retainCount1。这是为了减少对上下文
的依赖而引入的机制。


6nonatomic,非原子性访问,不加同步,多线程并发访问会提高性能。
注意,如果不加此属性,则默认是两个访问方法都为原子型事务访问。锁
被加到所属对象实例级。


// SPDX-License-Identifier: GPL-2.0-or-later /* * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. * * Copyright © 2001 Red Hat UK Limited * Copyright © 2001-2010 David Woodhouse <dwmw2@infradead.org> */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/init.h> #include <linux/vmalloc.h> #include <linux/of.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #include <linux/module.h> #ifdef MTD_SLPFLASH_NAND #include "../../sstar/slp_flash/mdrv_nand_partition.h" #endif #ifdef CONFIG_TP_TAPO_SPMINIOS #ifdef MTD_SLPFLASH_NAND /* nandflash可擦除块为128K */ #define SECTOR_VERIFY_SIZE 0x20000 #else #define SECTOR_VERIFY_SIZE 0x10000 #endif #endif /* tp header part defination , by Wu Yin, 27Mar14 */ /* modify for cloud router tp header, by Wu Yin, 28Jul14 */ #include "../../../arch/powerpc/boot/redboot.h" struct fis_image_desc { unsigned char name[16]; // Null terminated name uint32_t flash_base; // Address within FLASH of image uint32_t mem_base; // Address in memory where it executes uint32_t size; // Length of image uint32_t entry_point; // Execution entry point uint32_t data_length; // Length of actual data unsigned char _pad[256-(16+7*sizeof(uint32_t))]; uint32_t desc_cksum; // Checksum over image descriptor uint32_t file_cksum; // Checksum over image data }; struct fis_list { struct fis_image_desc *img; struct fis_list *next; }; /********add by zhangwu 24May07***************/ struct fis_image_desc flash_sectors[] = { { FACTORY_BOOT_MTD_NAME, }, { FACTORY_INFO_MTD_NAME, }, { RADIO_MTD_NAME, }, { UC_MTD_NAME, }, { NORMAL_BOOT_MTD_NAME, }, { KERNEL_MTD_NAME, }, { ROMFS_MTD_NAME, }, #ifndef CONFIG_MTD_ROOTFS_SPLIT { JFFS2_MTD_NAME, }, #endif #ifdef CONFIG_MTD_USER_RECORD_PARTS { USER_RECORD_MTD_NAME, }, #endif #ifdef CONFIG_TP_TAPO_SPMINIOS { VERIFY_MTD_NAME, }, #endif #ifdef CONFIG_TAPO_NAND_UPGRADE { UPBOOT_MTD_NAME, }, #endif { FIRMWARE_MTD_NAME, }, { UITRON_MTD_NAME, }, { UITRON_EXT_MTD_NAME, }, { LD_MTD_NAME, }, { ISP_MTD_NAME, }, { AF_MTD_NAME, }, { {0xff}, } }; struct fis_image_desc extra_flash_sectors[] = { { TP_HEADER_MTD_NAME, }, { KERNEL_AND_ROMFS_MTD_NAME, }, #ifdef CONFIG_MTD_ROOTFS_SPLIT { JFFS2_MTD_NAME, }, #endif { {0xff}, } }; static int directory = CONFIG_MTD_REDBOOT_DIRECTORY_BLOCK; module_param(directory, int, 0); static inline int redboot_checksum(struct fis_image_desc *img) { /* RedBoot doesn't actually write the desc_cksum field yet AFAICT */ return 1; } #ifdef MTD_SLPFLASH_NAND int nand_mtd_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); #endif #ifdef ENCRYPT_ROOTFS_HEADER #define AES_KEY_LEN 16 #define ENCRYPT_ROOTFS_HEADER_SIZE (512) int decrypt_rootfs_header_flg = 0; static unsigned char AES_CFB1_key[] = CONFIG_ENCRYPT_ROOTFS_KEY; static unsigned char AES_CFB1_iv[] = { 0x55, 0xAA, 0xDE, 0xAD, 0xC0, 0xDE, 'L', 'I', 'N', 'U', 'X', 'E', 'x', 'T', 0xAA, 0x55, }; long long rootfs_start = 0; unsigned char decrypt_rootfs_header_buffer[ENCRYPT_ROOTFS_HEADER_SIZE] = {0}; #include <crypto/aes.h> static int _AES_cfb1_decrypt(const unsigned char *key, int key_len, const unsigned char *ori_iv, const unsigned char *in, int in_len, unsigned char *out) { int err; struct crypto_aes_ctx ctx; int i, k, t, numBlocks; u8 block[16]; u8 iv[16]; memcpy(iv, ori_iv, 16); err = aes_expandkey(&ctx, key, key_len); if (err) { printk("cipher setkey failed: %d", err); return err; } numBlocks = in_len / 128; for (i = numBlocks; i > 0; i--) { memcpy(out, in, 16); for (k = 0; k < 128; k++) { aes_encrypt(&ctx, block, iv); for (t = 0; t < 15; t++) { iv[t] = (iv[t] << 1) | (iv[t + 1] >> 7); } iv[15] = (iv[15] << 1) | ((in[k >> 3] >> (7 - (k & 7))) & 1); out[k >> 3] ^= (block[0] & 0x80U) >> (k & 7); } out += 16; in += 16; } memzero_explicit(&ctx, sizeof(ctx)); return 128 * numBlocks; } static int decrypt_rootfs_header(struct mtd_info *master, int offset) { int ret = 0; size_t retlen = 0; unsigned char *plain = (unsigned char *)kmalloc(ENCRYPT_ROOTFS_HEADER_SIZE, GFP_KERNEL); rootfs_start = offset; if(!plain){ printk("decrypt_rootfs_header kmalloc fail\n"); goto exit; } #ifdef MTD_SLPFLASH_NAND nand_mtd_read(master, (loff_t)offset, (size_t)ENCRYPT_ROOTFS_HEADER_SIZE, &retlen, (void *)plain); #else master->_read(master, (loff_t)offset, (size_t)ENCRYPT_ROOTFS_HEADER_SIZE, &retlen, (void *)plain); #endif if(retlen != ENCRYPT_ROOTFS_HEADER_SIZE){ printk("decrypt_rootfs_header read fail\n"); goto exit; } ret = _AES_cfb1_decrypt(AES_CFB1_key, AES_KEY_LEN, AES_CFB1_iv, plain, ENCRYPT_ROOTFS_HEADER_SIZE<<3, decrypt_rootfs_header_buffer); if(ret != (ENCRYPT_ROOTFS_HEADER_SIZE<<3)){ printk("decrypt_rootfs_header decrypt fail ret:%d\n",ret); goto exit; } printk("decrypt_rootfs_header done\n"); decrypt_rootfs_header_flg = 1; exit: if(plain){ kfree(plain); } return ret; } #endif #if 0 static void parse_redboot_of(struct mtd_info *master) { struct device_node *np; struct device_node *npart; u32 dirblock; int ret; np = mtd_get_of_node(master); if (!np) return; npart = of_get_child_by_name(np, "partitions"); if (!npart) return; ret = of_property_read_u32(npart, "fis-index-block", &dirblock); if (ret) return; /* * Assign the block found in the device tree to the local * directory block pointer. */ directory = dirblock; } #endif static int parse_redboot_partitions(struct mtd_info *master, const struct mtd_partition **pparts, struct mtd_part_parser_data *data) { int nrparts = 0; struct fis_image_desc *buf; struct mtd_partition *parts; struct fis_list *fl = NULL, *tmp_fl; int ret, i; size_t retlen; char *names; char *nullname; int namelen = 0; int nulllen = 0; int numslots; #ifdef ENCRYPT_ROOTFS_HEADER int rootfs_base = 0; #endif // unsigned long offset; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED static char nullstring[] = "unallocated"; #endif #ifdef MTD_SLPFLASH_NAND /* TODO: Why let the size in nand_flash_sectors be parts[i].size ? */ extern struct fis_image_desc_nand nand_flash_sectors[]; #endif #if 0 /* donot read from redboot */ parse_redboot_of(master); if ( directory < 0 ) { offset = master->size + directory * master->erasesize; while (mtd_block_isbad(master, offset)) { if (!offset) { nogood: printk(KERN_NOTICE "Failed to find a non-bad block to check for RedBoot partition table\n"); return -EIO; } offset -= master->erasesize; } } else { offset = directory * master->erasesize; while (mtd_block_isbad(master, offset)) { offset += master->erasesize; if (offset == master->size) goto nogood; } } buf = vmalloc(master->erasesize); if (!buf) return -ENOMEM; printk(KERN_NOTICE "Searching for RedBoot partition table in %s at offset 0x%lx\n", master->name, offset); ret = mtd_read(master, offset, master->erasesize, &retlen, (void *)buf); if (ret) goto out; if (retlen != master->erasesize) { ret = -EIO; goto out; } #endif TP_HEADER hdr; /* Read mtd partion from flash hdr, add by lsz 30Nov07 */ /* Modify for SLP, by wuyin 11Feb14 */ #ifdef MTD_SLPFLASH_NAND printk("SLP flash nand read\n"); ret = nand_mtd_read(master, MTD_REDBOOT_TP_HEADER_ADDRESS, sizeof(hdr), &retlen, (void *)&hdr); #else printk("SLP flash nor read\n"); ret = master->_read(master, MTD_REDBOOT_TP_HEADER_ADDRESS, sizeof(hdr), &retlen, (void *)&hdr); #endif printk("MTD_REDBOOT_TP_HEADER_ADDRESS:0x%x\n", MTD_REDBOOT_TP_HEADER_ADDRESS); for (i = 0; i < sizeof(flash_sectors)/sizeof(struct fis_image_desc); i ++) { if (strcmp(flash_sectors[i].name, LD_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.ldOffset); flash_sectors[i].size = ntohl(hdr.ldLen); } else if (strcmp(flash_sectors[i].name, UITRON_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.uitronOffset); flash_sectors[i].size = ntohl(hdr.uitronLen); } else if (strcmp(flash_sectors[i].name, UITRON_EXT_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.uitronExtOffset); flash_sectors[i].size = ntohl(hdr.uitronExtLen); } else if (strcmp(flash_sectors[i].name, FACTORY_BOOT_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.factoryBootOffset); flash_sectors[i].size = ntohl(hdr.factoryBootLen); } else if (strcmp(flash_sectors[i].name, FACTORY_INFO_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.factoryInfoOffset); flash_sectors[i].size = ntohl(hdr.factoryInfoLen); } else if (strcmp(flash_sectors[i].name, NORMAL_BOOT_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.bootloaderOffset); flash_sectors[i].size = ntohl(hdr.bootloaderLen); } else if (strcmp(flash_sectors[i].name, KERNEL_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.kernelOffset); flash_sectors[i].size = ntohl(hdr.kernelLen); } #ifdef CONFIG_MTD_ROOTFS_SPLIT else if (strcmp(flash_sectors[i].name, ROMFS_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.romFsOffset); flash_sectors[i].size = ntohl(hdr.romFsLen) + ntohl(hdr.jffs2FsLen); #ifdef ENCRYPT_ROOTFS_HEADER rootfs_base = flash_sectors[i].flash_base; #endif } #else else if (strcmp(flash_sectors[i].name, ROMFS_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.romFsOffset); flash_sectors[i].size = ntohl(hdr.romFsLen); #ifdef ENCRYPT_ROOTFS_HEADER rootfs_base = flash_sectors[i].flash_base; #endif } else if (strcmp(flash_sectors[i].name, JFFS2_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.jffs2FsOffset); flash_sectors[i].size = ntohl(hdr.jffs2FsLen); } #endif #ifdef CONFIG_MTD_USER_RECORD_PARTS else if (strcmp(flash_sectors[i].name, USER_RECORD_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.jffs2FsOffset) + ntohl(hdr.jffs2FsLen); flash_sectors[i].size = MTD_USER_RECORD_PARTITION_LEN; } #endif #ifdef CONFIG_TP_TAPO_SPMINIOS else if (strcmp(flash_sectors[i].name, VERIFY_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.jffs2FsOffset) + ntohl(hdr.jffs2FsLen) #ifdef CONFIG_MTD_USER_RECORD_PARTS + MTD_USER_RECORD_PARTITION_LEN #endif ; flash_sectors[i].size = SECTOR_VERIFY_SIZE; } #endif #ifdef CONFIG_TAPO_NAND_UPGRADE else if (strcmp(flash_sectors[i].name, UPBOOT_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.upbootOffset) ; flash_sectors[i].size = ntohl(hdr.upbootLen); } #endif else if (strcmp(flash_sectors[i].name, UC_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.ucOffset); flash_sectors[i].size = ntohl(hdr.ucLen); } else if (strcmp(flash_sectors[i].name, AF_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.factoryInfoOffset) + ntohl(hdr.factoryInfoLen); flash_sectors[i].size = ntohl(hdr.radioOffset) - flash_sectors[i].flash_base; } else if (strcmp(flash_sectors[i].name, RADIO_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.radioOffset); flash_sectors[i].size = ntohl(hdr.radioLen); } else if (strcmp(flash_sectors[i].name, ISP_MTD_NAME) == 0) { flash_sectors[i].flash_base = ntohl(hdr.ispOffset); flash_sectors[i].size = ntohl(hdr.ispLen); } else if (strcmp(flash_sectors[i].name, FIRMWARE_MTD_NAME) == 0) { /* QSDK AP135 has "firmware" as the last mtd partition(seems for upgrade), add it here since basing on QSDK */ flash_sectors[i].flash_base = ntohl(hdr.tpHeaderOffset); flash_sectors[i].size = ntohl(hdr.tpHeaderLen) + ntohl(hdr.kernelLen) + ntohl(hdr.romFsLen) + ntohl(hdr.jffs2FsLen); } } for (i = 0; i < sizeof(extra_flash_sectors) / sizeof(struct fis_image_desc); i++) { if (strcmp(extra_flash_sectors[i].name, TP_HEADER_MTD_NAME) == 0) { extra_flash_sectors[i].flash_base = ntohl(hdr.tpHeaderOffset); extra_flash_sectors[i].size = ntohl(hdr.tpHeaderLen); } else if (strcmp(extra_flash_sectors[i].name, KERNEL_AND_ROMFS_MTD_NAME) == 0) { extra_flash_sectors[i].flash_base = ntohl(hdr.tpHeaderOffset); extra_flash_sectors[i].size = ntohl(hdr.tpHeaderLen) + ntohl(hdr.kernelLen) + ntohl(hdr.romFsLen); /* include TP header */ } #ifdef CONFIG_MTD_ROOTFS_SPLIT else if (strcmp(extra_flash_sectors[i].name, JFFS2_MTD_NAME) == 0) { extra_flash_sectors[i].flash_base = ntohl(hdr.jffs2FsOffset); extra_flash_sectors[i].size = ntohl(hdr.jffs2FsLen); } #endif } #ifdef ENCRYPT_ROOTFS_HEADER decrypt_rootfs_header(master, rootfs_base); #endif /************add by zhangwu 24May07********************/ buf = flash_sectors; printk(KERN_INFO "Searching for RedBoot partition table\n"); /******************************************************/ numslots = (master->erasesize / sizeof(struct fis_image_desc)); #if 0 /* donot find "FIS directory" */ for (i = 0; i < numslots; i++) { if (!memcmp(buf[i].name, "FIS directory", 14)) { /* This is apparently the FIS directory entry for the * FIS directory itself. The FIS directory size is * one erase block; if the buf[i].size field is * swab32(erasesize) then we know we are looking at * a byte swapped FIS directory - swap all the entries! * (NOTE: this is 'size' not 'data_length'; size is * the full size of the entry.) */ /* RedBoot can combine the FIS directory and config partitions into a single eraseblock; we assume wrong-endian if either the swapped 'size' matches the eraseblock size precisely, or if the swapped size actually fits in an eraseblock while the unswapped size doesn't. */ if (swab32(buf[i].size) == master->erasesize || (buf[i].size > master->erasesize && swab32(buf[i].size) < master->erasesize)) { int j; /* Update numslots based on actual FIS directory size */ numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); for (j = 0; j < numslots; ++j) { /* A single 0xff denotes a deleted entry. * Two of them in a row is the end of the table. */ if (buf[j].name[0] == 0xff) { if (buf[j].name[1] == 0xff) { break; } else { continue; } } /* The unsigned long fields were written with the * wrong byte sex, name and pad have no byte sex. */ swab32s(&buf[j].flash_base); swab32s(&buf[j].mem_base); swab32s(&buf[j].size); swab32s(&buf[j].entry_point); swab32s(&buf[j].data_length); swab32s(&buf[j].desc_cksum); swab32s(&buf[j].file_cksum); } } else if (buf[i].size < master->erasesize) { /* Update numslots based on actual FIS directory size */ numslots = buf[i].size / sizeof(struct fis_image_desc); } break; } } if (i == numslots) { /* Didn't find it */ printk(KERN_NOTICE "No RedBoot partition table detected in %s\n", master->name); ret = 0; goto out; } #endif for (i = 0; i < numslots; i++) { struct fis_list *new_fl, **prev; if (buf[i].name[0] == 0xff) { #if 0 if (buf[i].name[1] == 0xff) { break; } else { continue; } #endif break; } if (!redboot_checksum(&buf[i])) break; new_fl = kmalloc(sizeof(struct fis_list), GFP_KERNEL); namelen += strlen(buf[i].name)+1; if (!new_fl) { ret = -ENOMEM; goto out; } new_fl->img = &buf[i]; if (data && data->origin) buf[i].flash_base -= data->origin; else buf[i].flash_base &= master->size-1; /* I'm sure the JFFS2 code has done me permanent damage. * I now think the following is _normal_ */ prev = &fl; #if 0 /* donot sort partitions by flash_base, just according to flash_sectors */ while(*prev && (*prev)->img->flash_base < new_fl->img->flash_base) prev = &(*prev)->next; #endif while (*prev) prev = &(*prev)->next; new_fl->next = *prev; *prev = new_fl; nrparts++; } #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if (fl->img->flash_base) { nrparts++; nulllen = sizeof(nullstring); } for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) { if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) { nrparts++; nulllen = sizeof(nullstring); } } #endif parts = kzalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL); if (!parts) { printk("kzalloc parts failed\n"); ret = -ENOMEM; goto out; } nullname = (char *)&parts[nrparts]; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if (nulllen > 0) { strcpy(nullname, nullstring); } #endif names = nullname + nulllen; i=0; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if (fl->img->flash_base) { parts[0].name = nullname; parts[0].size = fl->img->flash_base; parts[0].offset = 0; i++; } #endif for ( ; i<nrparts; i++) { parts[i].size = fl->img->size; #ifdef MTD_SLPFLASH_NAND /* TODO: Why let the size in nand_flash_sectors be parts[i].size ? */ if (!strcmp(fl->img->name, ROMFS_MTD_NAME)) { int k = 0; for (k = 0; k < get_nand_flash_sectors_num(); k++) { if (!strcmp(nand_flash_sectors[k].name, ROMFS_MTD_NAME)) { parts[i].size = nand_flash_sectors[k].size; break; } } } #endif parts[i].offset = fl->img->flash_base; parts[i].name = names; strcpy(names, fl->img->name); #ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY if (!memcmp(names, "RedBoot", 8) || !memcmp(names, "RedBoot config", 15) || !memcmp(names, "FIS directory", 14)) { parts[i].mask_flags = MTD_WRITEABLE; } /* make "boot" and "art" partition readonly, by wuyin, 11Feb14 */ if (!memcmp(names, "boot", 5) || !memcmp(names, "art", 4)) { parts[i].mask_flags = MTD_WRITEABLE; } #endif names += strlen(names)+1; #ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { i++; parts[i].offset = parts[i-1].size + parts[i-1].offset; parts[i].size = fl->next->img->flash_base - parts[i].offset; parts[i].name = nullname; } #endif tmp_fl = fl; fl = fl->next; kfree(tmp_fl); } ret = nrparts; *pparts = parts; out: while (fl) { struct fis_list *old = fl; fl = fl->next; kfree(old); } // vfree(buf); return ret; } static const struct of_device_id mtd_parser_redboot_of_match_table[] = { { .compatible = "redboot-fis" }, {}, }; MODULE_DEVICE_TABLE(of, mtd_parser_redboot_of_match_table); static struct mtd_part_parser redboot_parser = { .parse_fn = parse_redboot_partitions, .name = "RedBoot", .of_match_table = mtd_parser_redboot_of_match_table, }; module_mtd_part_parser(redboot_parser); int get_info_from_mtd_partition_name(char *partition_name, int *offset, int *size) { int i = 0; for (i = 0; i < sizeof(flash_sectors) / sizeof(struct fis_image_desc); i++) { if (strcmp(flash_sectors[i].name, partition_name) == 0) { *offset = flash_sectors[i].flash_base; *size = flash_sectors[i].size; return 0; } } for (i = 0; i < sizeof(extra_flash_sectors) / sizeof(struct fis_image_desc); i++) { if (strcmp(extra_flash_sectors[i].name, partition_name) == 0) { *offset = extra_flash_sectors[i].flash_base; *size = extra_flash_sectors[i].size; return 0; } } return -1; } EXPORT_SYMBOL(get_info_from_mtd_partition_name); /* mtd parsers will request the module by parser name */ MODULE_ALIAS("RedBoot"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); MODULE_DESCRIPTION("Parsing code for RedBoot Flash Image System (FIS) tables"); 其中flash_sectors[]的内容是怎么填充的
10-15
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值