1、Segment是Buffer缓冲区存储数据的基本单位,每个Segment能存储的最大字节是8192也就是8k的数据
/** The size of all segments in bytes. */
static final int SIZE = 8192; 2、SHARE_MINIMUM是用来共享的阈值,超过1k的数据才可以进行共享
/** Segments will be shared when doing so avoids {@code arraycopy()} of this many bytes. */
static final int SHARE_MINIMUM = 1024; 3、Segment存储的数据是字节数组data
final byte[] data; 4、Segmen中有记录可读的位置的pos,每次读取数据的时候从这个下标开始
/** The next byte of application data byte to read in this segment. */
int pos; 5、Segmen中有记录可写的位置的limit,每次写数据的时候从这个下标开始
/** The first byte of available data ready to be written to. */
int limit; 6、boolean shared; 标识是否本Segment可以共享,实际就是浅拷贝,共享的是里面的data数据 7、boolean owner; 标识data是否是本Segment对象所拥有的的,副本的Segment对象只能读 8、因为Buffer数据的存储Segment的设计是采用双向链表的结构,因此就出现了Segment next;和 Segment prev;
   //两个构造函数
Segment() { this.data = new byte[SIZE];
this.owner = true;
this.shared = false;
} Segment(byte[] data, int pos, int limit, boolean shared, boolean owner) {
this.data = data;
this.pos = pos;
this.limit = limit;
this.shared = shared;
this.owner = owner;
} //浅拷贝一个Segmen对象,同时标记当前的Segment对象是可共享的、新的Segment对象owner是false
final Segment sharedCopy() {
shared = true;
return new Segment(data, pos, limit, true, false);
} //深拷贝-重建Segment,data数据也是新的
final Segment unsharedCopy() {
return new Segment(data.clone(), pos, limit, false, true);
}
  //把当前的Segment对象从链表中删除,并返回他的next,如果只有一个Segment
//也就是next==this,直接返回null
public final @Nullable Segment pop() {
Segment result = next != this ? next : null;
prev.next = next;
next.prev = prev;
next = null;
prev = null;
return result;
} //在当前Segment对象的后面新增一个next对象,并返回这个新增的Segment
public final Segment push(Segment segment) {
segment.prev = this;
segment.next = next;
next.prev = segment;
next = segment;
return segment;
}
 //把当前的Segment拆分成2个Segment对象
public final Segment split(int byteCount) {
//如果划分的长度是0或者超出了当前Segment的内容(我就存了3个长度东西,你让我划分长度是5,那我就报错)
if (byteCount <= 0 || byteCount > limit - pos) throw new IllegalArgumentException();
Segment prefix;
//如果长度超过了1字节,那么我就通过sharedCopy的浅拷贝方式创建一个新的Segment对象:此时我们两个对象的数据是相同的
//pos、limit也是相同的
if (byteCount >= SHARE_MINIMUM) {
prefix = sharedCopy();
} else {
//不超过可共享的阈值那就从池子里面去拿(典型的享元模式:可参考Handler中的Message设计模式)
prefix = SegmentPool.take();
//通过arraycopy的方式把data中的byteCount的字节从pos开始,拷贝到prefix.data中去,从0开始
System.arraycopy(data, pos, prefix.data, 0, byteCount);
}
//通过上面的方式我们就得到了一个新的Segment对象:pos可能是0也可能是当前Segment的pos,取决你的生成方式。
//新的Segment对象的limit就是pos+byteCount,表示可写的下标位置
prefix.limit = prefix.pos + byteCount;
//当前的Segment对象的pos就是pos+byteCount:因为被别人拿走了byteCount长度,limit的位置不变
pos += byteCount;
//把新的Segment对象放在prev的后面连起来,这样就形成了新的链接(prefix在当前Segment的前面,前Segment-Pre的后面)
prev.push(prefix);
//返回
return prefix;
  //整理空间:被分割的Segment或者被读取的Segment会出现pos不为0的情况,导致空间的浪费
public final void compact() {
//只有自己一个Segment这里抛出了异常,个人认为没有必要,不整理不就行了,干嘛给人家抛异常
if (prev == this) throw new IllegalStateException();
//副本没有操作权限
if (!prev.owner) return; // Cannot compact: prev isn't writable.
//需要移动的长度就是当前的有效内容的长度
int byteCount = limit - pos;
//计算下前一个Segment可用空间
int availableByteCount = SIZE - prev.limit + (prev.shared ? 0 : prev.pos);
//如果前一个Segment的剩余可用空间不足以放下当前的长度直接返回,无能为力
if (byteCount > availableByteCount) return; // Cannot compact: not enough writable space.
//如果前面的Segment可用剩余的空间能放得下就把byteCount长度的字节写到pre中
writeTo(prev, byteCount);
//写完之后自己的这个Segment就可以被弹出回收了
pop();
SegmentPool.recycle(this);
} 只是把当前的Segment内容移动到pre中

  //把当前Segment中byteCount长度的字节移到sink中
public final void writeTo(Segment sink, int byteCount) {
//副本是不能进行操作的
if (!sink.owner) throw new IllegalArgumentException();
//如果sink当前可写的位置+需要移动的长度超过了Segment中存储的最大值(不一定会抛异常哦,因为pos可能不是0,也就是前面都空余的空间)
if (sink.limit + byteCount > SIZE) {
// 如果sink是共享的不能写
if (sink.shared) throw new IllegalArgumentException();
//如果整个Segment可写的空间都不足以放下byteCount长度,那就是真的放不下了:抛异常
if (sink.limit + byteCount - sink.pos > SIZE) throw new IllegalArgumentException();
//虽然limt后面放不下,当时pos前面还有空间,加起来剩余的空间能放下,这时候整理下sink,进行一次移动
//从pos位置移动sink.limit - sink.pos长度当前有效内容,移动到从0开始的位置
System.arraycopy(sink.data, sink.pos, sink.data, 0, sink.limit - sink.pos);
//移动之后更新sink.limit的位置:向前挪了pos个长度
sink.limit -= sink.pos;
//移动之后更新sink.pos的位置:从pos向前挪了pos个位置,也就是0
sink.pos = 0;
} //整理完sink之后,或者sink.limit + byteCount能放得下byteCount个字节后,把当前Segment中的byteCount字节移动到sink中
System.arraycopy(data, pos, sink.data, sink.limit, byteCount);
//更新sink.limit的位置
sink.limit += byteCount;
//更新当前Segment的pos的位置:被拿走了了byteCount长度,所以需要后移byteCount个长度
pos += byteCount;
}

写入方法的总结:如果sink中limt之后的剩余空间能够写的下byteCount的长度直接通过arraycopy把data中的数据移动到sink的limt后面即可,然后更新sink的limit和当前Segment的pos

如果sink中limt之后的空间放不下byteCount长度der字节,就会判断sink中可用空间是否能放下,如果可以,自身进行一次移动,把pos之前的空间利用上,更新自身的pos和limt。最后在把data中数据通过arraycopy把data中的数据移动到sink的limt后面即可,然后更新sink的limit和当前Segment的pos。

  }

okio中数据存储的基本单位Segment的更多相关文章

  1. 第2/24周 页_SQL Server 中数据存储的基本单位

    上周通过探讨SQL Server如何执行一个查询奠定了基础.我也在那里提到页是8kb的缓存.今天我们对页进行进一步集中探讨,从性能调优角度挖掘出更多的细节. 页是SQL Server的基础,在SQL ...

  2. 第2周 页_SQL Server 中数据存储的基本单位

    原文:第2周 页_SQL Server 中数据存储的基本单位 上周通过探讨SQL Server如何执行一个查询奠定了基础.我也在那里提到页是8kb的缓存.今天我们对页进行进一步集中探讨,从性能调优角度 ...

  3. Android中数据存储(一)

    国庆没有给国家添堵,没有勾搭妹子,乖乖的写着自己的博客..... 本文将为大家介绍Android中数据存储的五种方式,数据存储可是非常重要的知识哦. 一,文件存储数据 ①在ROM存储数据 关于在ROM ...

  4. Java基础知识强化之IO流笔记46:IO流练习之 把文本文件中数据存储到集合中的案例

    1.  把文本文件中数据存储到集合中      需求:从文本文件中读取数据(每一行为一个字符串数据)到集合中,并遍历集合. 分析:      通过题目的意思我们可以知道如下的一些内容,      数据 ...

  5. android中数据存储

    android中数据存储     Android 中存储数据的方式有五种:SQLite数据库.文件存储.内容提供者.网络.SharedPreferences(Key----value)五种存储方式. ...

  6. Android中数据存储(四)——ContentProvider存储数据

    目录(?)[+]   当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方 ...

  7. Android中数据存储(三)——SQLite数据库存储数据

    当一个应用程序在Android中安装后,我们在使用应用的过程中会产生很多的数据,应用都有自己的数据,那么我们应该如何存储数据呢? 数据存储方式 Android 的数据存储有5种方式: 1. Share ...

  8. Android中数据存储之SharedPreferences

    import android.content.Context; import android.content.SharedPreferences; import android.content.Sha ...

  9. 在AndroidStudio中数据存储第三方数据管理Bmob的使用

    ---恢复内容开始--- 在日常写代码的过程中我们比较痛苦的就是数据库的建立和使用,那么今天来介绍一下一个第三方的数据管理平台Bmonb. 一.我们首先进入Bmob的官网创建一个账号 Bome官网网址 ...

  10. Java中数据存储分配

    (1)内存分配的策略 按照编译原理的观点,程序运行时的内存分配有三种策略,分别是静态的,栈式的,和堆式的. 静态存储分配是指在编译时就能确定每个数据目标在运行时刻的存储空间需求,因而在编 译时就可以给 ...

随机推荐

  1. 在gitlab仓库中cli 编译通不过 怎样解决

    1.先切换到master  分支 git  pull   (把代码拉下来) 2.再切换到当前分支 git  rebase   master 查看git  log 看是否跟新到最新的log 3.再在当前 ...

  2. Shell写脚本关于ssh执行jar包,需要刷新JDK路径的问题

    比如脚本中下面这一段 ssh $i "java -jar /applog/$PROJECT/$APPNAME --server.port=$SERVER_PORT >/dev/null ...

  3. NOI 顺序查找——查找特定的值

    描述 在一个序列(下标从1开始)中查找一个给定的值,输出第一次出现的位置. 输入 第一行包含一个正整数n,表示序列中元素个数.1 <= n <= 10000.第二行包含n个整数,依次给出序 ...

  4. connect及bind、listen、accept背后的三次握手

    如图所示打上断点,分别找出connect() bind() listen() accpet()对应的函数 源码在上一次作业中已经分析过了 https://www.cnblogs.com/qwertyu ...

  5. 什么是js严格模式?

    (use strict)严格模式是一种将更好的错误检查引入代码中的方法. 在使用严格模式时,无法使用隐式声明的变量.将值赋给只读属性或将属性添加到不可扩展的对象等. 1. 严格模式的目的 1) 消除J ...

  6. element ui el-date-picker format 和 value-format 格式不一致,会导致日期输入框不显示

    element ui el-date-picker和value-format 不一致导致输入框不显示

  7. 线程Thread小记

    1 public class ConcurentDemo extends Thread { 2 @Override 3 public void run() { 4 super.run(); 5 Sys ...

  8. JAVA加载PMML算法模型

    注:加载失败时尝试修改pmml文件版本为4.3 依赖 <dependency> <groupId>org.jpmml</groupId> <artifactI ...

  9. Spring Cloud Alibaba实现服务的无损下线功能

    目录 1.背景 2.解决方案 2.1 找到通过负载均衡组件获取可用服务信息的地方 2.2 解决思路 3.部分实现代码 3.1 引入jar 3.2 编写服务下线方法 3.3 监听配置变更,清除服务缓存 ...

  10. 分布式CAP_BASE博客参考

    https://blog.csdn.net/lixinkuan328/article/details/95535691 CAP 一致性(Consistency) 可用性(Availability) 分 ...