docs: public void copyFromDirectBuffer(int bytesRead) {
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.entry(tc, "copyFromDirectBuffer: " + bytesRead);
          }
  
          int newPosition = this.oWsBBDirect.position();
          int cachePosition = this.oByteBuffer.position();
  
          if (this.quickBufferAction == NOT_ACTIVATED) {
  
              // copy the bytes read from the old position
              this.oWsBBDirect.position(cachePosition);
  
              if (bytesRead == -1) {
                  // copy all the bytes up to the limit
                  this.oWsBBDirect.get(oByteBuffer.array(), cachePosition + oByteBuffer.arrayOffset(), oByteBuffer.remaining());
              } else if (bytesRead > 0) {
                  // copy just the bytes that were read
                  this.oWsBBDirect.get(oByteBuffer.array(), cachePosition + oByteBuffer.arrayOffset(), bytesRead);
              }
  
              // set to the position
              this.oByteBuffer.position(newPosition);
  
          } else {
              synchronized (wsBBRoot.actionAccess) {
                  if (wsBBRoot.actionState == PooledWsByteBufferImpl.COPY_WHEN_NEEDED_STATE1) {
  
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(tc, "actionState: STATE1 - nothing will be copied");
                      }
  
                      // don't do the copy now, just update the position pointer
                      this.oByteBuffer.position(newPosition);
  
                      int min = cachePosition;
                      int max = newPosition - 1;
  
                      // in STATE1 readMin and readMax keep track of the data that will
                      // need to be moved up to the nonDirect buffer upon entering STATE2.
                      // This area can be expanded to contain some bytes which were not
                      // read into, since nothing has been put into the NonDirect buffer,
                      // so any gaps in the read data will be uninitialized data anyway.
                      if ((wsBBRoot.readMin == -1) || (min < wsBBRoot.readMin)) {
                          wsBBRoot.readMin = min;
                      }
                      if ((wsBBRoot.readMax == -1) || (max > wsBBRoot.readMax)) {
                          wsBBRoot.readMax = max;
                      }
  
                      // if getMin and getMax are not outside of the new read, then reset them,
                      // so that the next get(...) will pull up the new data.
                      if ((wsBBRoot.getMax >= wsBBRoot.readMin)
                          && (wsBBRoot.getMin <= wsBBRoot.readMax)) {
  
                          wsBBRoot.getMax = -1;
                          wsBBRoot.getMin = -1;
                      }
  
                  } else {
                      // actionState must equal COPY_WHEN_NEEDED_STATE2 or
                      // BUFFER_MGMT_COPY_ALL_FINAL (if another thread just changed it).
                      //
  
                      if (wsBBRoot.actionState == PooledWsByteBufferImpl.COPY_WHEN_NEEDED_STATE2) {
                          if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                              Tr.debug(tc, "actionState: STATE2, first copy up any previously read data");
                          }
                          // first copy up to the nonDirect buffer any data that was read before
                          // entering STATE 2
                          if ((wsBBRoot.readMin != -1) && (wsBBRoot.readMax != -1)) {
                              moveUpUsingGetMinMax(wsBBRoot.readMin, wsBBRoot.readMax, 0);
                          }
                      }
  
                      this.wsBBRoot.actionState = PooledWsByteBufferImpl.COPY_ALL_FINAL;
                      this.quickBufferAction = NOT_ACTIVATED;
  
                      if (TraceComponent.isAnyTracingEnabled() && tc.isDebugEnabled()) {
                          Tr.debug(tc, "actionState: COPY_ALL_FINAL, copy currently read data to NonDirect buffer ");
                      }
  
                      // copy the bytes read from the old position
                      this.oWsBBDirect.position(cachePosition);
  
                      if (bytesRead == -1) {
                          // copy all the bytes up to the limit
                          this.oWsBBDirect.get(oByteBuffer.array(), cachePosition + oByteBuffer.arrayOffset(), oByteBuffer.remaining());
                      } else if (bytesRead > 0) {
                          // copy just the bytes that were read
                          this.oWsBBDirect.get(oByteBuffer.array(), cachePosition + oByteBuffer.arrayOffset(), bytesRead);
                      }
  
                      // set to the position
                      this.oByteBuffer.position(newPosition);
  
                  }
              } // end-sync
          }
  
          if (TraceComponent.isAnyTracingEnabled() && tc.isEntryEnabled()) {
              Tr.exit(tc, "copyFromDirectBuffer");
          }
      }