pub async fn write_or_append(
        &self,
        offset: Option<u64>,
        buf: &[u8],
        block_size: u64,
        reserver: &dyn StorageReservation,
        current_time: Option<Duration>,
        source: &dyn ReadObjectHandle,
    ) -> Result<(), Error> {
        let size = self.data.size();
        let offset = offset.unwrap_or(size);

        // |inner| shouldn't be modified until we're at a part of the function where nothing can
        // fail (either before an early-return, or at the end of the function).
        // TODO(jfsulliv): Consider splitting this up into a prepare and commit function (the
        // prepare function would need to return a lock).  That would make this requirement
        // more explicit, but might also have other advantages, such as not needing the
        // StorageReservation trait nor the Reservation wrapper any more.  However, it involves
        // other complexity, mostly around the lock.
        let mut dirtied_intervals = vec![];
        let mut dirtied_bytes = 0;
        let reservation;
        {
            let inner = self.inner.lock().unwrap();

            let end = offset + buf.len() as u64;
            let aligned_range = round_down(offset, block_size)..round_up(end, block_size).unwrap();

            let intervals = inner.intervals.get_intervals(&aligned_range).unwrap();
            // TODO(jfsulliv): This might be much simpler and more readable if we refactored
            // interval_tree to have an iterator interface.  See
            // https://fuchsia-review.googlesource.com/c/fuchsia/+/547024/comments/523de326_6e2b4766.
            let mut current_offset = aligned_range.start;
            let mut i = 0;
            while current_offset < end {
                let interval = intervals.get(i);
                if interval.is_none() {
                    // Passed the last known interval.
                    dirtied_bytes += aligned_range.end - current_offset;
                    dirtied_intervals.push(CachedRange {
                        range: current_offset..aligned_range.end,
                        state: CacheState::Dirty,
                    });
                    break;
                }

                let interval = interval.unwrap();
                assert!(interval.range.start % block_size == 0);
                assert!(interval.range.end % block_size == 0);
                if current_offset < interval.range.start {
                    // There's a hole before the next interval.
                    dirtied_bytes += interval.range.start - current_offset;
                    dirtied_intervals.push(CachedRange {
                        range: current_offset..interval.range.start,
                        state: CacheState::Dirty,
                    });
                    current_offset = interval.range.start;
                }

                // Writing over an existing interval.
                let next_end = std::cmp::min(interval.range.end, aligned_range.end);
                let overlap_range = current_offset..next_end;
                match &interval.state {
                    CacheState::Dirty => {
                        // The range is already dirty and has a reservation.  Nothing needs to be
                        // done.
                    }
                    CacheState::Flushing => {
                        // The range is flushing.  Since this is a new write, we need to reserve
                        // space for it, and mark the range as Dirty.
                        dirtied_bytes += overlap_range.end - overlap_range.start;
                        dirtied_intervals
                            .push(CachedRange { range: overlap_range, state: CacheState::Dirty })
                    }
                };
                current_offset = next_end;
                i += 1;
            }
            let reservation_needed = reserver.reservation_needed(inner.dirty_bytes + dirtied_bytes)
                - reserver.reservation_needed(inner.dirty_bytes);
            reservation = if reservation_needed > 0 {
                Some(reserver.reserve(reservation_needed)?)
            } else {
                None
            };
        }

        // TODO(csuter): This will need to change to support partial writes: when short of free
        // space it's possible that some of the write will succeed but not all.
        self.data.write(offset, buf, source).await?;

        // After this point, we're committing changes, so nothing should fail.
        let mut inner = self.inner.lock().unwrap();
        let end = offset + buf.len() as u64;
        if end > size {
            inner.content_size = Some(end);
        }
        for interval in dirtied_intervals {
            assert!(interval.range.start % block_size == 0);
            assert!(interval.range.end % block_size == 0);
            inner.intervals.add_interval(&interval).unwrap();
        }
        if let Some(reservation) = reservation {
            // The cache implicitly owns the reservation in dirty_bytes.
            reservation.take();
        }
        inner.dirty_bytes += dirtied_bytes;
        inner.modification_time = current_time;
        Ok(())
    }
/// Writes some new data into the cache, marking that data as Dirty.  If |offset| is None, the
/// data is appended to the end of the existing data.  If |current_time| is set (as a duration
/// since the UNIX epoch in UTC, with whatever granularity the filesystem supports), the cached
/// mtime will be set to this value.  If the filesystem doesn't support timestamps, it may
/// simply set this to None.  Returns the size after the write completes.  This is not
/// thread-safe; the caller is responsible for making sure that only one thread is mutating the
/// cache at any point in time.
Writes some new data into the cache, marking that data as Dirty.  If |offset| is None, the
data is appended to the end of the existing data.  If |current_time| is set (as a duration
since the UNIX epoch in UTC, with whatever granularity the filesystem supports), the cached
mtime will be set to this value.  If the filesystem doesn't support timestamps, it may
simply set this to None.  Returns the size after the write completes.  This is not
thread-safe; the caller is responsible for making sure that only one thread is mutating the
cache at any point in time.
Writes some new data into the cache marking that data as Dirty . If |offset| is None the data is appended to the end of the existing data . If |current_time| is set ( as a duration since the UNIX epoch in UTC with whatever granularity the filesystem supports ) the cached mtime will be set to this value . If the filesystem doesn ' t support timestamps it may simply set this to None . Returns the size after the write completes . This is not thread - safe ; the caller is responsible for making sure that only one thread is mutating the cache at any point in time .
pub async fn write_or_append(
        &self,
        offset: Option<u64>,
        buf: &[u8],
        block_size: u64,
        reserver: &dyn StorageReservation,
        current_time: Option<Duration>,
        source: &dyn ReadObjectHandle,
    ) -> Result<(), Error> {
        let size = self.data.size();
        let offset = offset.unwrap_or(size);
        // |inner| shouldn't be modified until we're at a part of the function where nothing can
        // fail (either before an early-return, or at the end of the function).
        // TODO(jfsulliv): Consider splitting this up into a prepare and commit function (the
        // prepare function would need to return a lock).  That would make this requirement
        // more explicit, but might also have other advantages, such as not needing the
        // StorageReservation trait nor the Reservation wrapper any more.  However, it involves
        // other complexity, mostly around the lock.
        let mut dirtied_intervals = vec![];
        let mut dirtied_bytes = 0;
        let reservation;
        {
            let inner = self.inner.lock().unwrap();
            let end = offset + buf.len() as u64;
            let aligned_range = round_down(offset, block_size)..round_up(end, block_size).unwrap();
            let intervals = inner.intervals.get_intervals(&aligned_range).unwrap();
            // TODO(jfsulliv): This might be much simpler and more readable if we refactored
            // interval_tree to have an iterator interface.  See
            // https://fuchsia-review.googlesource.com/c/fuchsia/+/547024/comments/523de326_6e2b4766.
            let mut current_offset = aligned_range.start;
            let mut i = 0;
            while current_offset < end {
                let interval = intervals.get(i);
                if interval.is_none() {
                    // Passed the last known interval.
                    dirtied_bytes += aligned_range.end - current_offset;
                    dirtied_intervals.push(CachedRange {
                        range: current_offset..aligned_range.end,
                        state: CacheState::Dirty,
                    });
                    break;
                }
                let interval = interval.unwrap();
                assert!(interval.range.start % block_size == 0);
                assert!(interval.range.end % block_size == 0);
                if current_offset < interval.range.start {
                    // There's a hole before the next interval.
                    dirtied_bytes += interval.range.start - current_offset;
                    dirtied_intervals.push(CachedRange {
                        range: current_offset..interval.range.start,
                        state: CacheState::Dirty,
                    });
                    current_offset = interval.range.start;
                }
                // Writing over an existing interval.
                let next_end = std::cmp::min(interval.range.end, aligned_range.end);
                let overlap_range = current_offset..next_end;
                match &interval.state {
                    CacheState::Dirty => {
                        // The range is already dirty and has a reservation.  Nothing needs to be
                        // done.
                    }
                    CacheState::Flushing => {
                        // The range is flushing.  Since this is a new write, we need to reserve
                        // space for it, and mark the range as Dirty.
                        dirtied_bytes += overlap_range.end - overlap_range.start;
                        dirtied_intervals
                            .push(CachedRange { range: overlap_range, state: CacheState::Dirty })
                    }
                };
                current_offset = next_end;
                i += 1;
            }
            let reservation_needed = reserver.reservation_needed(inner.dirty_bytes + dirtied_bytes)
                - reserver.reservation_needed(inner.dirty_bytes);
            reservation = if reservation_needed > 0 {
                Some(reserver.reserve(reservation_needed)?)
            } else {
                None
            };
        }
        // TODO(csuter): This will need to change to support partial writes: when short of free
        // space it's possible that some of the write will succeed but not all.
        self.data.write(offset, buf, source).await?;
        // After this point, we're committing changes, so nothing should fail.
        let mut inner = self.inner.lock().unwrap();
        let end = offset + buf.len() as u64;
        if end > size {
            inner.content_size = Some(end);
        }
        for interval in dirtied_intervals {
            assert!(interval.range.start % block_size == 0);
            assert!(interval.range.end % block_size == 0);
            inner.intervals.add_interval(&interval).unwrap();
        }
        if let Some(reservation) = reservation {
            // The cache implicitly owns the reservation in dirty_bytes.
            reservation.take();
        }
        inner.dirty_bytes += dirtied_bytes;
        inner.modification_time = current_time;
        Ok(())
    }
pub async fn write_or_append ( & self , offset : Option < u64 > , buf : & [ u8 ] , block_size : u64 , reserver : & dyn StorageReservation , current_time : Option < Duration > , source : & dyn ReadObjectHandle , ) -> Result < ( ) , Error > { let size = self . data . size ( ) ; let offset = offset . unwrap_or ( size ) ; // |inner| shouldn't be modified until we're at a part of the function where nothing can // fail (either before an early-return, or at the end of the function). // TODO(jfsulliv): Consider splitting this up into a prepare and commit function (the // prepare function would need to return a lock).  That would make this requirement // more explicit, but might also have other advantages, such as not needing the // StorageReservation trait nor the Reservation wrapper any more.  However, it involves // other complexity, mostly around the lock. let mut dirtied_intervals = vec ! [ ] ; let mut dirtied_bytes = 0 ; let reservation ; { let inner = self . inner . lock ( ) . unwrap ( ) ; let end = offset + buf . len ( ) as u64 ; let aligned_range = round_down ( offset , block_size ) .. round_up ( end , block_size ) . unwrap ( ) ; let intervals = inner . intervals . get_intervals ( & aligned_range ) . unwrap ( ) ; // TODO(jfsulliv): This might be much simpler and more readable if we refactored // interval_tree to have an iterator interface.  See // https://fuchsia-review.googlesource.com/c/fuchsia/+/547024/comments/523de326_6e2b4766. let mut current_offset = aligned_range . start ; let mut i = 0 ; while current_offset < end { let interval = intervals . get ( i ) ; if interval . is_none ( ) { // Passed the last known interval. dirtied_bytes += aligned_range . end - current_offset ; dirtied_intervals . push ( CachedRange { range : current_offset .. aligned_range . end , state : CacheState :: Dirty , } ) ; break ; } let interval = interval . unwrap ( ) ; assert ! ( interval range start block_size 0 ) ; assert ! ( interval range end block_size 0 ) ; if current_offset < interval . range . start { // There's a hole before the next interval. dirtied_bytes += interval . range . start - current_offset ; dirtied_intervals . push ( CachedRange { range : current_offset .. interval . range . start , state : CacheState :: Dirty , } ) ; current_offset = interval . range . start ; } // Writing over an existing interval. let next_end = std :: cmp :: min ( interval . range . end , aligned_range . end ) ; let overlap_range = current_offset .. next_end ; match & interval . state { CacheState :: Dirty => { // The range is already dirty and has a reservation.  Nothing needs to be // done. } CacheState :: Flushing => { // The range is flushing.  Since this is a new write, we need to reserve // space for it, and mark the range as Dirty. dirtied_bytes += overlap_range . end - overlap_range . start ; dirtied_intervals . push ( CachedRange { range : overlap_range , state : CacheState :: Dirty } ) } } ; current_offset = next_end ; i += 1 ; } let reservation_needed = reserver . reservation_needed ( inner . dirty_bytes + dirtied_bytes ) - reserver . reservation_needed ( inner . dirty_bytes ) ; reservation = if reservation_needed > 0 { Some ( reserver . reserve ( reservation_needed ) ? ) } else { None } ; } // TODO(csuter): This will need to change to support partial writes: when short of free // space it's possible that some of the write will succeed but not all. self . data . write ( offset , buf , source ) . await ? ; // After this point, we're committing changes, so nothing should fail. let mut inner = self . inner . lock ( ) . unwrap ( ) ; let end = offset + buf . len ( ) as u64 ; if end > size { inner . content_size = Some ( end ) ; } for interval in dirtied_intervals { assert ! ( interval range start block_size 0 ) ; assert ! ( interval range end block_size 0 ) ; inner . intervals . add_interval ( & interval ) . unwrap ( ) ; } if let Some ( reservation ) = reservation { // The cache implicitly owns the reservation in dirty_bytes. reservation . take ( ) ; } inner . dirty_bytes += dirtied_bytes ; inner . modification_time = current_time ; Ok ( ( ) ) }
Writes some new data into the cache, marking that data as Dirty.
Writes some new data into the cache marking that data as Dirty .
&self
offset
Option<u64>
buf
&[u8]
block_size
u64
reserver
&dyn StorageReservation
current_time
Option<Duration>
source
&dyn ReadObjectHandle
&self
offset
Option<u64>
buf
&[u8]
block_size
u64
reserver
&dyn StorageReservation
current_time
Option<Duration>
source
&dyn ReadObjectHandle